前端Js筆試題面試題

前言

為了方便現(xiàn)在和以后前端學(xué)習(xí)和面試,在此收集和整理了Js相關(guān)的筆試面試題,供自己查閱的同時(shí),希望也會(huì)對(duì)大家有所幫助。

目錄:

前端HTML+CSS筆試題面試題

前端Vue筆試題面試題

前端小程序筆試題面試題

數(shù)據(jù)類型

JS的基本數(shù)據(jù)類型

Undefined、Null、Boolean、Number、String新增:Symbol

JS有哪些內(nèi)置對(duì)象?

ObjectJavaScript 中所有對(duì)象的父對(duì)象

數(shù)據(jù)封裝類對(duì)象:Object、Array、Boolean、NumberString

其他對(duì)象:Function、Arguments、Math、Date、RegExp、Error

JS中使用typeof能得到的哪些類型?(考點(diǎn):JS變量類型)

typeof undefinded //undefined
typeof null // object
typeof'abc' // string
typeof 123 // number
typeof [] // object
typeof {} // object
typeof true //boolean
typeof b // b 沒有聲明,但是還會(huì)顯示 undefined

何時(shí)使用===何時(shí)使用==?(考點(diǎn):強(qiáng)制類型轉(zhuǎn)換)

== 比較兩個(gè)值相等返回ture

=== 比較兩者的值和類型都相等才返回true 嚴(yán)格相等

0,NAN,null,undefinded,'',falseif語句中會(huì)強(qiáng)制轉(zhuǎn)化為 false

if (obj.a == null) {
    // 這里相當(dāng)于 obj.a === null || obj.a ===undefinded, 簡寫形式
    // 這里jquery 源碼中推薦的寫法
}

null,undefined 的區(qū)別?

1、nullNull類型,代表“空值”,代表一個(gè)空對(duì)象指針,使用typeof運(yùn)算得到 “object”,所以你可以認(rèn)為它是一個(gè)特殊的對(duì)象值。

2、undefinedUndefined類型,當(dāng)一個(gè)聲明了一個(gè)變量未初始化時(shí),得到的就是undefined。

ps:一句話簡單來說nullundefine值比較是相等,但類型不同。

Javascript創(chuàng)建對(duì)象的幾種方式?

1、對(duì)象字面量的方式

var = {}

2、通過構(gòu)造函數(shù)方式創(chuàng)建。

var obj = new Object(); 

3、通過Object.create()方式創(chuàng)建。

var obj = Object.create(Object.prototype);

翻轉(zhuǎn)一個(gè)字符串

可以先將字符串轉(zhuǎn)成一個(gè)數(shù)組,然后用數(shù)組的reverse()+join()方法。

let str="hello word";
let b=[...str].reverse().join("");//drow olleh

描述new一個(gè)對(duì)象的過程

1、創(chuàng)建一個(gè)新對(duì)象

2、this指向這個(gè)對(duì)象

3、執(zhí)行代碼,即對(duì)this賦值

4、隱式返回this

JS按存儲(chǔ)方式區(qū)分變量類型

// 值類型
var a = 10
var b = a
a = 11
console.log(b) // 10

// 引用類型
var obj1 = {x : 100}
var obj2 = obj1
obj1.x = 200
console.log(obj2) // 200

如何判斷一個(gè)變量是對(duì)象還是數(shù)組?

1、instanceof方法
instanceof運(yùn)算符是用來測試一個(gè)對(duì)象是否在其原型鏈原型構(gòu)造函數(shù)的屬性

var arr = []; 
arr instanceof Array; // true

2、constructor方法
constructor屬性返回對(duì)創(chuàng)建此對(duì)象的數(shù)組函數(shù)的引用,就是返回對(duì)象相對(duì)應(yīng)的構(gòu)造函數(shù)

var arr = []; 
arr.constructor == Array; //true

3、最簡單的方法,這種寫法兼容性最好使用Object.prototype.toString.call()

function isObjArr(value){
     if (Object.prototype.toString.call(value) === "[object Array]") {
            console.log('value是數(shù)組');
       }else if(Object.prototype.toString.call(value)==='[object Object]'){//這個(gè)方法兼容性好一點(diǎn)
            console.log('value是對(duì)象');
      }else{
          console.log('value不是數(shù)組也不是對(duì)象')
      }
}

4、ES5新增方法isArray()

var a = new Array(123);
var b = new Date();
console.log(Array.isArray(a)); //true
console.log(Array.isArray(b)); //false

ps:千萬不能使用typeof來判斷對(duì)象和數(shù)組,因?yàn)檫@兩種類型都會(huì)返回"object"

如何對(duì)一個(gè)數(shù)組去重?

1、Set結(jié)構(gòu)去重(ES6用法)。

ES6提供了新的數(shù)據(jù)結(jié)構(gòu)Set。它類似于數(shù)組,但是成員的值都是唯一的,沒有重復(fù)的值。

[...new Set(array)];

2、遍歷,將值添加到新數(shù)組,用indexOf()判斷值是否存在,已存在就不添加,達(dá)到去重效果。

  let a = ['1','2','3',1,NaN,NaN,undefined,undefined,null,null, 'a','b','b'];
    let unique= arr =>{
         let newA=[];
        arr.forEach(key => {
           if( newA.indexOf(key)<0 ){ //遍歷newA是否存在key,如果存在key會(huì)大于0就跳過push的那一步
             newA.push(key);
           }
        });
        return newA;
    }
    console.log(unique(a)) ;//["1", "2", "3", 1, NaN, NaN, undefined, null, "a", "b"]
// 這個(gè)方法不能分辨NaN,會(huì)出現(xiàn)兩個(gè)NaN。是有問題的,下面那個(gè)方法好一點(diǎn)。

3、利用for嵌套for,然后splice去重(ES5中最常用)。

function unique(arr){            
        for(var i=0; i<arr.length; i++){
            for(var j=i+1; j<arr.length; j++){
                if(arr[i]==arr[j]){         //第一個(gè)等同于第二個(gè),splice方法刪除第二個(gè)
                    arr.splice(j,1);
                    j--;
                }
            }
        }
return arr;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
    console.log(unique(arr))
    //[1, "true", 15, false, undefined, NaN, NaN, "NaN", "a", {…}, {…}]     //NaN和{}沒有去重,兩個(gè)null直接消失了

4、forEach遍歷,然后利用Object.keys(對(duì)象)返回這個(gè)對(duì)象可枚舉屬性組成的數(shù)組,這個(gè)數(shù)組就是去重后的數(shù)組。

    let a = ['1', '2', '3', 1,NaN,NaN,undefined,undefined,null,null, 'a', 'b', 'b'];
    const unique = arr => {
        var obj = {}
        arr.forEach(value => {
            obj[value] = 0;//這步新添加一個(gè)屬性,并賦值,如果不賦值的話,屬性會(huì)添加不上去
        })
        return Object.keys(obj);//`Object.keys(對(duì)象)`返回這個(gè)對(duì)象可枚舉屬性組成的數(shù)組,這個(gè)數(shù)組就是去重后的數(shù)組
    }
    console.log(unique(a));//["1", "2", "3", "NaN", "undefined", "null", "a", "b"]
    

作用域和閉包

var、let、const之間的區(qū)別

var聲明變量可以重復(fù)聲明,而let不可以重復(fù)聲明

var是不受限于塊級(jí)的,而let是受限于塊級(jí)

var會(huì)與window相映射(會(huì)掛一個(gè)屬性),而let不與window相映射

var可以在聲明的上面訪問變量,而let有暫存死區(qū),在聲明的上面訪問變量會(huì)報(bào)錯(cuò)

const聲明之后必須賦值,否則會(huì)報(bào)錯(cuò)

const定義不可變的量,改變了就會(huì)報(bào)錯(cuò)

constlet一樣不會(huì)與window相映射、支持塊級(jí)作用域、在聲明的上面訪問變量會(huì)報(bào)錯(cuò)

說明This幾種不同的使用場景

1、作為構(gòu)造函數(shù)執(zhí)行

2、作為對(duì)象屬性執(zhí)行

3、作為普通函數(shù)執(zhí)行

4、call apply bind

談?wù)?code>This對(duì)象的理解

1、this總是指向函數(shù)的直接調(diào)用者(而非間接調(diào)用者)

2、如果有new關(guān)鍵字,this指向new出來的那個(gè)對(duì)象

3、在事件中,this指向觸發(fā)這個(gè)事件的對(duì)象,特殊的是,IE中的attachEvent中的this總是指向全局對(duì)象Window

作用域

ES5作用域分為 全局作用域 和 函數(shù)作用域。

ES6新增塊級(jí)作用域,塊作用域由 { }包括,if語句和 for語句里面的{ }也屬于塊作用域。

// 塊級(jí)作用域
if (true) {
    var name = 'zhangsan'
}
console.log(name)
// 函數(shù)和全局作用域
var a = 100
function fn () {
    var a = 200
    console.log('fn', a)
}

JS創(chuàng)建10個(gè)<a>標(biāo)簽,點(diǎn)擊的時(shí)候彈出來對(duì)應(yīng)的序號(hào)?(考點(diǎn):作用域)

var i 
for (i = 0; i < 10; i++) {
    (function (i) {
        var a = document.createElement('a')
        a.innerHTML = i + '<br>'
        a.addEventListener('click', function (e) {
            e.preventDefault()
            alert(i)
        })
        document.body.appendChild(a)
    })(i)
}

說說你對(duì)作用域鏈的理解

1、作用域鏈的作用是保證執(zhí)行環(huán)境里有權(quán)訪問的變量和函數(shù)是有序的,作用域鏈的變量只能向上訪問,變量訪問到window對(duì)象即被終止,作用域鏈向下訪問變量是不被允許的

2、簡單的說,作用域就是變量與函數(shù)的可訪問范圍,即作用域控制著變量與函數(shù)的可見性和生命周期

3、通俗來說,一般情況下,變量取值到 創(chuàng)建 這個(gè)變量 的函數(shù)的作用域中取值。
但是如果在當(dāng)前作用域中沒有查到值,就會(huì)向上級(jí)作用域去查,直到查到全局作用域,這么一個(gè)查找過程形成的鏈條就叫做作用域鏈

var a = 100
function fn () {
    var b = 200
    // 當(dāng)前作用域沒有定于的變量,即‘自由變量’
    console.log(a)
    console.log(b)
}
fn()
console.log(a) 去父級(jí)作用域找a自由變量 作用域鏈

閉包

閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)

閉包是指有權(quán)訪問另一個(gè)函數(shù)作用域中變量的函數(shù),創(chuàng)建閉包的最常見的方式就是在一個(gè)函數(shù)內(nèi)創(chuàng)建另一個(gè)函數(shù),通過另一個(gè)函數(shù)訪問這個(gè)函數(shù)的局部變量,利用閉包可以突破作用鏈域

function F1 () {
    var a = 100
    // 返回一個(gè)函數(shù),函數(shù)作為返回值
    return function () {
        console.log(a)
    }
}
// f1 得到一個(gè)函數(shù)
var f1 = F1()
var a = 200
f1()  // 100 定義的時(shí)候父級(jí)作用域

閉包的特性

函數(shù)內(nèi)再嵌套函數(shù)

內(nèi)部函數(shù)可以引用外層的參數(shù)和變量

參數(shù)和變量不會(huì)被垃圾回收機(jī)制回收

說說你對(duì)閉包的理解

使用閉包主要是為了設(shè)計(jì)私有的方法和變量。閉包的優(yōu)點(diǎn)是可以避免全局變量的污染,缺點(diǎn)是閉包會(huì)常駐內(nèi)存,會(huì)增大內(nèi)存使用量,使用不當(dāng)很容易造成內(nèi)存泄露。在js中,函數(shù)即閉包,只有函數(shù)才會(huì)產(chǎn)生作用域的概念

閉包的最大用處有兩個(gè),一個(gè)是可以讀取函數(shù)內(nèi)部的變量,另一個(gè)就是讓這些變量始終保持在內(nèi)存中

閉包的另一個(gè)用處,是封裝對(duì)象的私有屬性和私有方法

好處:能夠?qū)崿F(xiàn)封裝和緩存等;

壞處:就是消耗內(nèi)存、不正當(dāng)使用會(huì)造成內(nèi)存溢出的問題

使用閉包的注意點(diǎn)

由于閉包會(huì)使得函數(shù)中的變量都被保存在內(nèi)存中,內(nèi)存消耗很大,所以不能濫用閉包,否則會(huì)造成網(wǎng)頁的性能問題,在IE中可能導(dǎo)致內(nèi)存泄露
解決方法是,在退出函數(shù)之前,將不使用的局部變量全部刪除

閉包的使用場景

閉包的兩個(gè)場景:函數(shù)作為返回值 和 函數(shù)作為參數(shù)傳遞

function F1 () {
    var a = 100
    // 返回一個(gè)函數(shù),函數(shù)作為返回值
    return function () {
        console.log(a)
    }
}
// f1 得到一個(gè)函數(shù)
var f1 = F1()
var a = 200
f1()  // 100 定義的時(shí)候父級(jí)作用域

function F2 (fn) {
    var a = 200
    fn()
}
F2(f1)

實(shí)際開發(fā)中閉包的應(yīng)用

// 閉包實(shí)際應(yīng)用中主要用于封裝變量 收斂權(quán)限
function isFirstLoad() {
    var _list = []
    return function (id) {
        if (_list.indexOf(id) >= 0) {
            return false
        } else {
            _list.push(id)
            return true
        }
    }
}
// 使用
var firstLoad = new isFirstLoad()
firstLoad(10) //true
firstLoad(10) //false
firstLoad(30) //true

原型和原型鏈

JavaScript原型,原型鏈 ? 有什么特點(diǎn)?

每個(gè)對(duì)象都會(huì)在其內(nèi)部初始化一個(gè)屬性,就是prototype(原型),
當(dāng)我們訪問一個(gè)對(duì)象的屬性時(shí),如果這個(gè)對(duì)象內(nèi)部不存在這個(gè)屬性,那么他就會(huì)去prototype里找這個(gè)屬性,這個(gè)prototype又會(huì)有自己的prototype,于是就這樣一直找下去,也就是我們平時(shí)所說的原型鏈的概念

<font color='red'>原型和原型鏈關(guān)系</font>

<b>關(guān)系:</b>instance.constructor.prototype = instance.__proto__

<font color='red'>原型和原型鏈特點(diǎn)</font>

JavaScript對(duì)象是通過引用來傳遞的,我們創(chuàng)建的每個(gè)新對(duì)象實(shí)體中并沒有一份屬于自己的原型副本。當(dāng)我們修改原型時(shí),與之相關(guān)的對(duì)象也會(huì)繼承這一改變
當(dāng)我們需要一個(gè)屬性的時(shí),Javascript引擎會(huì)先看當(dāng)前對(duì)象中是否有這個(gè)屬性, 如果沒有的
就會(huì)查找他的Prototype對(duì)象是否有這個(gè)屬性,如此遞推下去,一直檢索到 Object 內(nèi)建對(duì)象

<font color='red'>PS:</font>
1.所有的引用類型(數(shù)組,對(duì)象,函數(shù))都具有對(duì)象的特性,即可自由擴(kuò)展屬性(除了‘null’)除外

2.所有的引用類型(數(shù)組,對(duì)象,函數(shù))都有一個(gè)_proto_(隱式原型)屬性,屬性值是一個(gè)普通對(duì)象

3.所有的函數(shù),都有一個(gè)prototype(顯示原型)的屬性,也是一個(gè)普通對(duì)象

4.所有的引用類型(數(shù)組,對(duì)象,函數(shù))_proto_屬性值指向他的構(gòu)造的‘prototype’ 屬性值

當(dāng)試圖得到一個(gè)對(duì)象的某個(gè)屬性時(shí),如果這個(gè)對(duì)象本身沒有這個(gè)屬性,那么會(huì)去它的_proto_(即它的構(gòu)造函數(shù)的prototype) 中尋找

// 構(gòu)造函數(shù)
function Foo(name, age){
    this.name = name 
}
Foo.prototype.alertName = function () {
    alert(this.name)
}
//創(chuàng)建實(shí)例
var f = new Foo('zhangsan')
f.printName = function () {
    console.log(this.name)
}
//測試
f.printName()
f.alertName()

循環(huán)對(duì)象自身的屬性

var item
for (item in f) {
    // 高級(jí)瀏覽器已經(jīng)在 for in 中屏蔽了來自原型的屬性
    // 但是這里建議大家還是加上這個(gè)判斷,保證程序的健壯性
    if (f.hasOwnProperty(item)) {
        console.log(item)
    }
}

原型鏈

f.toString() //到f._proto_._proto_ 中去找

異步和單線程

同步和異步的區(qū)別是什么?分別舉一個(gè)同步和異步的例子

同步交互:指發(fā)送一個(gè)請求,需要等待返回,然后才能夠發(fā)送下一個(gè)請求,有個(gè)等待過程。

異步交互:指發(fā)送一個(gè)請求,不需要等待返回,隨時(shí)可以再發(fā)送下一個(gè)請求,即不需要等待。

相同的地方:都屬于交互方式,都是發(fā)送請求。

不同的地方:一個(gè)需要等待,一個(gè)不需要等待。

<b>簡單而言</b>,同步就是必須一件一件的做事,等前一件事做完后才能做下一件事。而異步這是把事情指派給別人后,接著繼續(xù)做下一件事,不必等別人返回的結(jié)果。

<b> 舉例:</b>

1、廣播,就是一個(gè)異步例子。發(fā)起者不關(guān)心接收者的狀態(tài)。不需要等待接收者的返回信息;
在部分情況下,我們的項(xiàng)目開發(fā)中都會(huì)優(yōu)先選擇不需要等待的異步交互方式。

2、電話,就是一個(gè)同步例子。發(fā)起者需要等待接收者,接通電話后,通信才開始。需要等待接收者的返回信息
比如銀行的轉(zhuǎn)賬系統(tǒng),對(duì)數(shù)據(jù)庫的保存操作等等,都會(huì)使用同步交互操作。

<font color='red'>ps: </font>alert是同步,setTimeoutsetInterval是異步,同步會(huì)阻塞代碼執(zhí)行,而異步不會(huì)

異步編程的實(shí)現(xiàn)方式?

1、回調(diào)函數(shù)
<Br>優(yōu)點(diǎn):簡單、容易理解

缺點(diǎn):不利于維護(hù),代碼耦合高

2、事件監(jiān)聽(采用時(shí)間驅(qū)動(dòng)模式,取決于某個(gè)事件是否發(fā)生):

優(yōu)點(diǎn):容易理解,可以綁定多個(gè)事件,每個(gè)事件可以指定多個(gè)回調(diào)函數(shù)

缺點(diǎn):事件驅(qū)動(dòng)型,流程不夠清晰

3、發(fā)布/訂閱(觀察者模式)

類似于事件監(jiān)聽,但是可以通過‘消息中心’,了解現(xiàn)在有多少發(fā)布者,多少訂閱者

4、Promise對(duì)象

優(yōu)點(diǎn):可以利用then方法,進(jìn)行鏈?zhǔn)綄懛?;可以書寫錯(cuò)誤時(shí)的回調(diào)函數(shù);

缺點(diǎn):編寫和理解,相對(duì)比較難

5、Generator函數(shù)

優(yōu)點(diǎn):函數(shù)體內(nèi)外的數(shù)據(jù)交換、錯(cuò)誤處理機(jī)制

缺點(diǎn):流程管理不方便

6、async函數(shù)

優(yōu)點(diǎn):內(nèi)置執(zhí)行器、更好的語義、更廣的適用性、返回的是Promise、結(jié)構(gòu)清晰。

缺點(diǎn):錯(cuò)誤處理機(jī)制

定時(shí)器的執(zhí)行順序或機(jī)制。

簡單來說:<b>因?yàn)?code>js是單線程的,瀏覽器遇到setTimeout或者setInterval會(huì)先執(zhí)行完當(dāng)前的代碼塊,在此之前會(huì)把定時(shí)器推入瀏覽器的待執(zhí)行事件隊(duì)列里面,等到瀏覽器執(zhí)行完當(dāng)前代碼之后會(huì)看一下事件隊(duì)列里面有沒有任務(wù),有的話才執(zhí)行定時(shí)器的代碼。</b> 所以即使把定時(shí)器的時(shí)間設(shè)置為0還是會(huì)先執(zhí)行當(dāng)前的一些代碼。

一個(gè)關(guān)于setTimeout的筆試題

console.log(1)
setTimeout(function () {
    console.log(2)
},0)
console.log(3)
setTimeout(function () {
console.log(4)
},1000)
console.log(5)
//結(jié)果為 1 3 5 2 4

前段使用異步的場景

1、定時(shí)任務(wù):setTimeout,setInverval

2、網(wǎng)絡(luò)請求:ajax請求,動(dòng)態(tài)<img>加載

3、事件綁定

數(shù)組和對(duì)象API

map與forEach的區(qū)別?

1、forEach方法,是最基本的方法,就是遍歷與循環(huán),默認(rèn)有3個(gè)傳參:分別是遍歷的數(shù)組內(nèi)容item、數(shù)組索引index、和當(dāng)前遍歷數(shù)組Array

2、map方法,基本用法與forEach一致,但是不同的,它會(huì)返回一個(gè)新的數(shù)組,所以在callback需要有return值,如果沒有,會(huì)返回undefined

JS 數(shù)組和對(duì)象的遍歷方式,以及幾種方式的比較

通常我們會(huì)用循環(huán)的方式來遍歷數(shù)組。但是循環(huán)是 導(dǎo)致js 性能問題的原因之一。一般我們會(huì)采用下幾種方式來進(jìn)行數(shù)組的遍歷

for in循環(huán)

<font color='red'>for循環(huán)</font>

<font color='red'>forEach</font>

1、這里的 forEach回調(diào)中兩個(gè)參數(shù)分別為 value,index
<BR>2、forEach 無法遍歷對(duì)象

3、IE不支持該方法;Firefoxchrome 支持

4、forEach 無法使用 break,continue 跳出循環(huán),且使用 return 是跳過本次循環(huán)

5、可以添加第二個(gè)參數(shù),為一個(gè)數(shù)組,回調(diào)中的this會(huì)指向這個(gè)數(shù)組,若沒有添加,則是指向window

<font color='red'>在方式一中</font>,for-in需要分析出array的每個(gè)屬性,這個(gè)操作性能開銷很大。用在 key 已知的數(shù)組上是非常不劃算的。所以盡量不要用for-in,除非你不清楚要處理哪些屬性,例如JSON對(duì)象這樣的情況

<font color='red'>在方式2中</font>,循環(huán)每進(jìn)行一次,就要檢查一下數(shù)組長度。讀取屬性(數(shù)組長度)要比讀局部變量慢,尤其是當(dāng)array里存放的都是 DOM元素,因?yàn)槊看巫x取都會(huì)掃描一遍頁面上的選擇器相關(guān)元素,速度會(huì)大大降低

寫一個(gè)能遍歷對(duì)象和數(shù)組的通用forEach函數(shù)

function forEach(obj, fn) {
    var key
    // 判斷類型
    if (obj instanceof Array) {
        obj.forEach(function (item, index) {
            fn(index, item)
        })
    } else {
        for (key in obj) {
            fn ( key, obj[key])
        }
    }
}

數(shù)組API

map: 遍歷數(shù)組,返回回調(diào)返回值組成的新數(shù)組
forEach: 無法break,可以用try/catch中throw new Error來停止
filter: 過濾
some: 有一項(xiàng)返回true,則整體為true
every: 有一項(xiàng)返回false,則整體為false
join: 通過指定連接符生成字符串
push / pop: 末尾推入和彈出,改變原數(shù)組, 返回推入/彈出項(xiàng)【有誤】
unshift / shift: 頭部推入和彈出,改變原數(shù)組,返回操作項(xiàng)【有誤】
sort(fn) / reverse: 排序與反轉(zhuǎn),改變原數(shù)組
concat: 連接數(shù)組,不影響原數(shù)組, 淺拷貝
slice(start, end): 返回截?cái)嗪蟮男聰?shù)組,不改變原數(shù)組
splice(start, number, value...): 返回刪除元素組成的數(shù)組,value 為插入項(xiàng),改變原數(shù)組
indexOf / lastIndexOf(value, fromIndex): 查找數(shù)組項(xiàng),返回對(duì)應(yīng)的下標(biāo)
reduce / reduceRight(fn(prev, cur), defaultPrev): 兩兩執(zhí)行,prev 為上次化簡函數(shù)的return值,cur 為當(dāng)前值(從第二項(xiàng)開始)

對(duì)象API

var obj = {
    x: 100,
    y: 200,
    z: 300
} 
var key
for (key in obj) {
    // 注意這里的 hasOwnProperty,再講原型鏈的時(shí)候講過
    if (obj.hasOwnProperty(key)) {
        console.log(key, obj[key])
    }
}

日期和隨機(jī)數(shù)

獲取2020-06-10格式的日期

Date.now() // 獲取當(dāng)前時(shí)間毫秒數(shù)
var dt = new Date()
dt.getTime() //獲取毫秒數(shù)
dt.getFullYear() // 年
dt.getMonth() // 月 (0-11)
dt.getDate() // 日 (0-31)
dt.getHours() // 小時(shí) (0-23)
dt.getMinutes() //分鐘 (0-59)
dt.getSeconds() //秒 (0-59)

ps: ES6 新增padStart(),padEnd()方法可用來返回06格式日期

獲取隨機(jī)數(shù),要求是長度一致的字符串格式

var random = Math.random()
var random = random + '0000000000'
console.log(random)
var random = random.slice(0, 10)
console.log(random)

DOMBOM操作

DOM是哪種基本的數(shù)據(jù)結(jié)構(gòu)

DOM是一種樹形結(jié)構(gòu)的數(shù)據(jù)結(jié)構(gòu)

DOM操作的常用API有哪些

1、獲取DOM節(jié)點(diǎn),以及節(jié)點(diǎn)的propertyAttribute

2、獲取父節(jié)點(diǎn),獲取子節(jié)點(diǎn)

3、新增節(jié)點(diǎn),刪除節(jié)點(diǎn)

DOM節(jié)點(diǎn)的Attributeproperty有何區(qū)別

property只是一個(gè)JS對(duì)象的屬性修改和獲取

Attribute是對(duì)html標(biāo)簽屬性的修改和獲取

DOM的節(jié)點(diǎn)操作

<b> 創(chuàng)建新節(jié)點(diǎn)</b>

createDocumentFragment()    //創(chuàng)建一個(gè)DOM片段
createElement()   //創(chuàng)建一個(gè)具體的元素
createTextNode()   //創(chuàng)建一個(gè)文本節(jié)點(diǎn)

<b>添加、移除、替換、插入</b>

appendChild()      //添加
removeChild()      //移除
replaceChild()      //替換
insertBefore()      //插入

<b> 查找</b>

getElementsByTagName()    //通過標(biāo)簽名稱
getElementsByName()     //通過元素的Name屬性的值
getElementById()        //通過元素Id,唯一性

<b> 如何檢測瀏覽器的類型</b>

可以通過檢測navigator.userAgent 在通過不通瀏覽器的不通來檢測

var ua = navigator.userAgent
var isChrome = ua.indexOf('Chrome')
console.log(isChrome)

<b> 拆解url的各部分</b>

使用location里面的location.href location.protocol location.pathname location.search location.hash來獲取各種參數(shù)

console.log(location.href)
console.log(location.host) //域名
console.log(location.protocol)
console.log(location.pathname) //路徑
console.log(location.search) // 參數(shù)
console.log(location.hash) 

// history
history.back()
history.forward()

事件機(jī)制

請解釋什么是事件代理

事件代理(Event Delegation),又稱之為事件委托。是 JavaScript 中常用綁定事件的常用技巧。顧名思義,“事件代理”即是把原本需要綁定的事件委托給父元素,讓父元素?fù)?dān)當(dāng)事件監(jiān)聽的職務(wù)。事件代理的原理是DOM元素的事件冒泡。

<b>使用事件代理的好處是:</b>

1、可以提高性能

2、可以大量節(jié)省內(nèi)存占用,減少事件注冊,比如在table上代理所有tdclick事件就非常棒

3、可以實(shí)現(xiàn)當(dāng)新增子對(duì)象時(shí)無需再次對(duì)其綁定

事件模型

W3C中定義事件的發(fā)生經(jīng)歷三個(gè)階段:捕獲階段(capturing)、目標(biāo)階段(targetin)、冒泡階段(bubbling)

冒泡型事件:當(dāng)你使用事件冒泡時(shí),子級(jí)元素先觸發(fā),父級(jí)元素后觸發(fā)

捕獲型事件:當(dāng)你使用事件捕獲時(shí),父級(jí)元素先觸發(fā),子級(jí)元素后觸發(fā)

DOM事件流:<時(shí)支持兩種事件模型:捕獲型事件和冒泡型事件

阻止冒泡:W3c中,使用stopPropagation()方法;在IE下設(shè)置cancelBubble = true

阻止捕獲:阻止事件的默認(rèn)行為,例如click - <a>后的跳轉(zhuǎn)。在W3c中,使用preventDefault()方法,在IE下設(shè)置window.event.returnValue = false

編寫一個(gè)通用的事件監(jiān)聽函數(shù)

function bindEvent(elem,type,selector,fn){
    if(fn==null){
        fn = selector;
        selector = null
    }
    elem.addEventListener(type,function(e){
        var target;
        if(selector){
            target = e.target;
            if(target.matches(selector)){
                fn.call(target,e)
            }
        }else{
            fn(e)
        }
    })
}

描述事件冒泡流程

當(dāng)給某元素綁定一個(gè)事件的時(shí)候,首先會(huì)觸發(fā)自己綁定的,然后會(huì)逐層向上級(jí)查找事件,這就是事件冒泡

var p1 = document.getElementById('p1')
        var body = document.body
        function bindEvent(elem, type, fn) {
            elem.addEventListener(type, fn)
        } 
        bindEvent(p1, 'click', function (e) {
                e.stopPropagation()
                var target = e.target
                alert("激活")
            })
            bindEvent(body, 'click', function (e) {
                var target = e.target
                alert("取消")
            })

對(duì)于一個(gè)無限下拉加載圖片的頁面,如何給每個(gè)圖片綁定事件

可以使用代理,通過對(duì)父級(jí)元素綁定一個(gè)事件,通過判斷事件的target屬性來進(jìn)行判斷,添加行為

var div1 = document.getElementById('div1')
        var div2 = document.getElementById('div2')
        function bindEvent(elem, type, fn) {
            elem.addEventListener(type, fn)
        } 
        bindEvent(div1, 'click', function (e) {
                var target = e.target
                alert(target.innerHTML)
            })
        bindEvent(div2, 'click', function (e) {
                var target = e.target
                alert(target.innerHTML)
        })

AJAX

Ajax原理

Ajax的原理簡單來說是在用戶和服務(wù)器之間加了—個(gè)中間層(AJAX引擎),通過XmlHttpRequest對(duì)象來向服務(wù)器發(fā)異步請求,從服務(wù)器獲得數(shù)據(jù),然后用javascript來操作DOM而更新頁面。使用戶操作與服務(wù)器響應(yīng)異步化。這其中最關(guān)鍵的一步就是從服務(wù)器獲得請求數(shù)據(jù)
<BR>Ajax的過程只涉及JavaScript、XMLHttpRequest和DOM。<font color='red'>XMLHttpRequest</font>是ajax的核心機(jī)制

手動(dòng)編寫一個(gè)ajax,不依賴第三方庫

// 1. 創(chuàng)建連接
    var xhr = null;
    xhr = new XMLHttpRequest()
    // 2. 連接服務(wù)器
    xhr.open('get', url, true)
    // 3. 發(fā)送請求
    xhr.send(null);
    // 4. 接受請求
    xhr.onreadystatechange = function(){
        if(xhr.readyState == 4){
            if(xhr.status == 200){
                success(xhr.responseText);
            } else { // fail
                fail && fail(xhr.status);
            }
        }
    }

ajax的優(yōu)點(diǎn)和缺點(diǎn)

<font color='red'>ajax的優(yōu)點(diǎn)</font>

1、無刷新更新數(shù)據(jù)(在不刷新整個(gè)頁面的情況下維持與服務(wù)器通信)

2、異步與服務(wù)器通信(使用異步的方式與服務(wù)器通信,不打斷用戶的操作)

3、前端和后端負(fù)載均衡(將一些后端的工作交給前端,減少服務(wù)器與寬度的負(fù)擔(dān))

4、界面和應(yīng)用相分離(ajax將界面和應(yīng)用分離也就是數(shù)據(jù)與呈現(xiàn)相分離)

<font color='red'>ajax的缺點(diǎn)</font>

1、ajax不支持瀏覽器back按鈕

2、安全問題 Aajax暴露了與服務(wù)器交互的細(xì)節(jié)

3、對(duì)搜索引擎的支持比較弱

4、破壞了BackHistory后退按鈕的正常行為等瀏覽器機(jī)制。

什么情況下會(huì)碰到跨域問題?有哪些解決方法?

跨域問題是這是瀏覽器為了安全實(shí)施的同源策略導(dǎo)致的,同源策略限制了來自不同源的<font color='red'>document、腳本</font>,同源的意思就是兩個(gè)URL的域名、協(xié)議、端口要完全相同。

script標(biāo)簽jsonp跨域、nginx反向代理、node.js中間件代理跨域、后端在頭部信息設(shè)置安全域名、后端在服務(wù)器上設(shè)置cors。

ps:有三個(gè)標(biāo)簽允許跨域加載資源:
<img src=xxx> <link href = xxx> <script src=xxx>

三個(gè)標(biāo)簽場景:

<img> 用于打點(diǎn)統(tǒng)計(jì),統(tǒng)計(jì)網(wǎng)站可能是其他域

<link> <script> 可以使用CDN,CDN的也是其他域

<script>可以用于JSONP

<b> ??跨域注意事項(xiàng)</b>

所有的跨域請求都必須經(jīng)過信息提供方允許

如果未經(jīng)允許即可獲取,那么瀏覽器同源策略出現(xiàn)漏洞

XMLJSON的區(qū)別?

<b>數(shù)據(jù)體積方面</b>

JSON相對(duì)于XML來講,數(shù)據(jù)的體積小,傳遞的速度更快些。

<b>數(shù)據(jù)交互方面</b>

JSONJavaScript的交互更加方便,更容易解析處理,更好的數(shù)據(jù)交互

<b>數(shù)據(jù)描述方面</b>

JSON對(duì)數(shù)據(jù)的描述性比XML較差

<b>傳輸速度方面</b>

JSON的速度要遠(yuǎn)遠(yuǎn)快于XML

getpost的區(qū)別

1、getpostHTTP中都代表著請求數(shù)據(jù),其中g(shù)et請求相對(duì)來說更簡單、快速,效率高些

2、get相對(duì)post安全性低

3、get有緩存,post沒有

4、get體積小,post可以無限大

5、geturl參數(shù)可見,post不可見

6、get只接受ASCII字符的參數(shù)數(shù)據(jù)類型,post沒有限制

7、get請求參數(shù)會(huì)保留歷史記錄,post中參數(shù)不會(huì)保留

8、get會(huì)被瀏覽器主動(dòng)catch,post不會(huì),需要手動(dòng)設(shè)置

9、get在瀏覽器回退時(shí)無害,post會(huì)再次提交請求

<font color='red'>什么時(shí)候使用post?</font>

<b>post一般用于修改服務(wù)器上的資源,對(duì)所發(fā)送的信息沒有限制。比如</b>

1、無法使用緩存文件(更新服務(wù)器上的文件或數(shù)據(jù)庫)

2、向服務(wù)器發(fā)送大量數(shù)據(jù)(POST 沒有數(shù)據(jù)量限制)

3、發(fā)送包含未知字符的用戶輸入時(shí),POSTGET 更穩(wěn)定也更可靠

TCP建立連接為什么需要三次?

  • 為了實(shí)現(xiàn)可靠數(shù)據(jù)傳輸, TCP 協(xié)議的通信雙方, 都必須維護(hù)一個(gè)序列號(hào), 以標(biāo)識(shí)發(fā)送出去的數(shù)據(jù)包中, 哪些是已經(jīng)被對(duì)方收到的。 三次握手的過程即是通信雙方相互告知序列號(hào)起始值, 并確認(rèn)對(duì)方已經(jīng)收到了序列號(hào)起始值的必經(jīng)步驟
  • 如果只是兩次握手, 至多只有連接發(fā)起方的起始序列號(hào)能被確認(rèn), 另一方選擇的序列號(hào)則得不到確認(rèn)

ES6

ES5的繼承和ES6的繼承有什么區(qū)別?

ES5的繼承時(shí)通過prototype或構(gòu)造函數(shù)機(jī)制來實(shí)現(xiàn)。<b>ES5的繼承實(shí)質(zhì)上是先創(chuàng)建子類的實(shí)例對(duì)象,然后再將父類的方法添加到this上</b>(Parent.apply(this))。

ES6的繼承機(jī)制完全不同,<b>實(shí)質(zhì)上是先創(chuàng)建父類的實(shí)例對(duì)象this(所以必須先調(diào)用父類的super()方法),然后再用子類的構(gòu)造函數(shù)修改this</b>。

具體的:ES6通過class關(guān)鍵字定義類,里面有構(gòu)造方法,類之間通過extends關(guān)鍵字實(shí)現(xiàn)繼承。子類必須在constructor方法中調(diào)用super方法,否則新建實(shí)例報(bào)錯(cuò)。因?yàn)樽宇悰]有自己的this對(duì)象,而是繼承了父類的this對(duì)象,然后對(duì)其進(jìn)行加工。如果不調(diào)用super方法,子類得不到this對(duì)象。

ps:super關(guān)鍵字指代父類的實(shí)例,即父類的this對(duì)象。在子類構(gòu)造函數(shù)中,調(diào)用super后,才可使用this關(guān)鍵字,否則報(bào)錯(cuò)

javascript如何實(shí)現(xiàn)繼承?

1、構(gòu)造繼承

2、原型繼承

3、實(shí)例繼承

4、拷貝繼承

<font color='red'>原型prototype機(jī)制或apply和call方法去實(shí)現(xiàn)較簡單,建議使用構(gòu)造函數(shù)與原型混合方式</font>

function Parent(){
        this.name = 'wang';
    }
    function Child(){
        this.age = 28;
    }
    Child.prototype = new Parent();//繼承了Parent,通過原型

    var demo = new Child();
    alert(demo.age);
    alert(demo.name);//得到被繼承的屬性
  }

談?wù)勀銓?duì)ES6的理解

新增模板字符串(為JavaScript提供了簡單的字符串插值功能)

箭頭函數(shù)

for-of(用來遍歷數(shù)據(jù)—例如數(shù)組中的值。)

arguments對(duì)象可被不定參數(shù)和默認(rèn)參數(shù)完美代替。

ES6promise對(duì)象納入規(guī)范,提供了原生的Promise對(duì)象。

增加了let和const命令,用來聲明變量。

增加了塊級(jí)作用域。

let命令實(shí)際上就增加了塊級(jí)作用域。

還有就是引入module模塊的概念

談一談箭頭函數(shù)與普通函數(shù)的區(qū)別?

  • 函數(shù)體內(nèi)的this對(duì)象,就是定義時(shí)所在的對(duì)象,而不是使用時(shí)所在的對(duì)象
  • 不可以當(dāng)作構(gòu)造函數(shù),也就是說,不可以使用new命令,否則會(huì)拋出一個(gè)錯(cuò)誤
  • 不可以使用arguments對(duì)象,該對(duì)象在函數(shù)體內(nèi)不存在。如果要用,可以用Rest參數(shù)代替
  • 不可以使用yield命令,因此箭頭函數(shù)不能用作Generator函數(shù)

forEach、for in、for of三者區(qū)別

forEach更多的用來遍歷數(shù)

for in一般常用來遍歷對(duì)象或json

for of數(shù)組對(duì)象都可以遍歷,遍歷對(duì)象需要通過和Object.keys()

for in循環(huán)出的是key,for of循環(huán)出的是value

Set、Map的區(qū)別

應(yīng)用場景Set用于數(shù)據(jù)重組,Map用于數(shù)據(jù)儲(chǔ)存

<b>Set:</b>

1,成員不能重復(fù)

2,只有鍵值沒有鍵名,類似數(shù)組

3,可以遍歷,方法有add, delete,has

<b> Map:</b>

1,本質(zhì)上是健值對(duì)的集合,類似集合

2,可以遍歷,可以跟各種數(shù)據(jù)格式轉(zhuǎn)換

promise對(duì)象的用法,手寫一個(gè)promise

promise是一個(gè)構(gòu)造函數(shù),下面是一個(gè)簡單實(shí)例

var promise = new Promise((resolve,reject) => {
    if (操作成功) {
        resolve(value)
    } else {
        reject(error)
    }
})
promise.then(function (value) {
    // success
},function (value) {
    // failure
})

請描述一下Promise的使用場景,'Promise'它所解決的問題以及現(xiàn)在對(duì)于異步操作的解決方案。

Promise的使用場景:ajax請求,回調(diào)函數(shù),復(fù)雜操作判斷。

PromiseES6為了解決異步編程所誕生的。

異步操作解決方案:Promise、Generator、定時(shí)器(不知道算不算)、還有ES7async

ECMAScript6 怎么寫class,為什么會(huì)出現(xiàn)class這種東西?

這個(gè)語法糖可以讓有OOP基礎(chǔ)的人更快上手js,至少是一個(gè)官方的實(shí)現(xiàn)了
但對(duì)熟悉js的人來說,這個(gè)東西沒啥大影響;一個(gè)Object.creat()搞定繼承,比class簡潔清晰的多

算法和其他

冒泡排序

每次比較相鄰的兩個(gè)數(shù),如果后一個(gè)比前一個(gè)小,換位置

var arr = [3, 1, 4, 6, 5, 7, 2];

function bubbleSort(arr) {
for (var i = 0; i < arr.length - 1; i++) {
    for(var j = 0; j < arr.length - i - 1; j++) {
        if(arr[j + 1] < arr[j]) {
            var temp;
            temp = arr[j];
            arr[j] = arr[j + 1];
            arr[j + 1] = temp;
        }
    }
}
return arr;
}
console.log(bubbleSort(arr));

快速排序

采用二分法,取出中間數(shù),數(shù)組每次和中間數(shù)比較,小的放到左邊,大的放到右邊

var arr = [3, 1, 4, 6, 5, 7, 2];
function quickSort(arr) {
    if(arr.length == 0) {
        return [];    // 返回空數(shù)組
    }

    var cIndex = Math.floor(arr.length / 2);
    var c = arr.splice(cIndex, 1);
    var l = [];
    var r = [];

    for (var i = 0; i < arr.length; i++) {
        if(arr[i] < c) {
            l.push(arr[i]);
        } else {
            r.push(arr[i]);
        }
    }

    return quickSort(l).concat(c, quickSort(r));
}
console.log(quickSort(arr));

懶加載

<img id="img1" src="preview.png" data-realsrc = "abc.png"/>
<script type = "text/javascript">
var img1 = document.getElementById("img1")
img1.src = img1.getAttribute('data-realsrc')
</script>

緩存DOM查詢

// 未緩存 DOM查詢
var i
for (i = 0; i < document.getElementByTagName('p').length; i++) {
    //todo
}

//緩存了DOM查詢
var pList = document.getElementByTagName('p')
var i 
for (i= 0; i < pList.length; i++) {
    // todo
}

合并DOM插入

var listNode = document.getElementById('list')

//要插入10個(gè)li標(biāo)簽
var frag = document.createDocumentFragment();
var x,li
for (x = 0; x < 10; x++) {
    li = document.createElement('li')
    li.innerHTML = "List item" + x
    frag.appendChild(li)
}
listNode.appendChild(frag)

事件節(jié)流

var textarea = document.getElementById('text')
var timeoutId
textarea.addEventListener('keyup', function () {
    if (timeoutId) {
        clearTimeout(timeoutId)
    }
    timeoutId = setTimeout(function () {
        //觸發(fā)事件    
    },100)
})

盡早操作

window.addEventListener('load', function () {
    //頁面的全部資源加載完才會(huì)去執(zhí)行,包括圖片,視頻
})
document.addEventListener('DOMContentLoaded', function() {
    //DOM 渲染完即可執(zhí)行,此時(shí)圖片,視頻還可能沒有加載完成
})

淺拷貝

首先可以通過 Object.assign 來解決這個(gè)問題

let a = {
    age: 1
}
let b = Object.assign({}, a)
a.age = 2
console.log(b.age) // 1

深拷貝

這個(gè)問題通??梢酝ㄟ^JSON.parse(JSON.stringify(object)) 來解決

let a = {
    age: 1,
    jobs: {
        first: 'FE'
    }
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE

數(shù)組降維

[1, [2], 3].flatMap(v => v)
// -> [1, 2, 3]

如果想將一個(gè)多維數(shù)組徹底的降維,可以這樣實(shí)現(xiàn)

const flattenDeep = (arr) => Array.isArray(arr)
  ? arr.reduce( (a, b) => [...a, ...flattenDeep(b)] , [])
  : [arr]

flattenDeep([1, [[2], [3, [4]], 5]])

預(yù)加載

在開發(fā)中,可能會(huì)遇到這樣的情況。有些資源不需要馬上用到,但是希望盡早獲取,這時(shí)候就可以使用預(yù)加載
預(yù)加載其實(shí)是聲明式的 fetch,強(qiáng)制瀏覽器請求資源,并且不會(huì)阻塞 onload 事件,可以使用以下代碼開啟預(yù)加載

<link rel="preload" >

預(yù)加載可以一定程度上降低首屏的加載時(shí)間,因?yàn)榭梢詫⒁恍┎挥绊懯灼恋匾奈募雍蠹虞d,唯一缺點(diǎn)就是兼容性不好

預(yù)渲染

可以通過預(yù)渲染將下載的文件預(yù)先在后臺(tái)渲染,可以使用以下代碼開啟預(yù)渲染

<link rel="prerender" >

性能優(yōu)化

JavaScript性能優(yōu)化

1、盡可能把 <script> 標(biāo)簽放在 body 之后,避免 JS 的執(zhí)行卡住 DOM 的渲染,最大程度保證頁面盡快地展示出來
2、盡可能合并 JS 代碼:提取公共方法,進(jìn)行面向?qū)ο笤O(shè)計(jì)等……
3、CSS 能做的事情,盡量不用 JS 來做,畢竟 JS 的解析執(zhí)行比較粗暴,而 CSS 效率更高。
4、盡可能逐條操作 DOM,并預(yù)定好 CSs 樣式,從而減少 reflow 或者 repaint 的次數(shù)。
5、盡可能少地創(chuàng)建 DOM,而是在 HTML 和 CSS 中使用 display: none 來隱藏,按需顯示。
6、壓縮文件大小,減少資源下載負(fù)擔(dān)。

JavaScript幾條基本規(guī)范

1、不要在同一行聲明多個(gè)變量
2、請使用===/!==來比較true/false或者數(shù)值
3、使用對(duì)象字面量替代new Array這種形式
4、不要使用全局變量
5、Switch語句必須帶有default分支
6、函數(shù)不應(yīng)該有時(shí)候有返回值,有時(shí)候沒有返回值
7、For循環(huán)必須使用大括號(hào)
8、IF語句必須使用大括號(hào)
9、for-in循環(huán)中的變量 應(yīng)該使用var關(guān)鍵字明確限定作用域,從而避免作用域污染
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容