JavaScript:面試頻繁出現的幾個易錯點

1.前言

這段時間,金三銀四,不少人面試,不少人分享面試題。在前段時間,我也臨時擔任面試官,爲了大概瞭解面試者的水平,我也寫了一份題目,面試了幾個前端開發者。在這段時間裏面,我在學,在寫設計模式的一些知識,想不到的設計模式的這些知識,就是面試題裏面,頻繁讓人掉坑的考點。因此,今天就總結一下,那些讓人掉坑的考點。javascript

2.面向對象編程

關於面向對象和麪向過程,我的以爲這二者不是絕對獨立的,而是相互相成的關係。至於何時用面向對象,何時用面向過程,具體狀況,具體分析。html

針對於面向對象編程的。知乎上有一個高贊回答:前端

面向對象: 狗.吃(屎)
面向過程: 吃.(狗,屎)java

可是這個例子以爲不太優雅,我改一下了,舉一個優雅些的小例子說明一下面向對象和麪向過程的區別。面試

需求:定義‘守候吃火鍋編程

面向對象的思想是:守候.動做(吃火鍋)設計模式

面向過程的思想是:動做(守候,吃火鍋)數組

代碼實現方面:微信

//面向對象
//定義人(姓名)
let People=function(name){
    this.name=name;
}
//動做
People.prototype={
    eat:function(someThing){
        console.log(`${this.name}${someThing}`);
    }
}
//守候是我的,因此要建立一我的(new一次People)
let shouhou=new People('守候','男',24);
shouhou.eat('火鍋');

//面向過程
let eat=function(who,someThing){
    console.log(`${who}${someThing}`);
}
eat('守候','火鍋');複製代碼

結果都同樣,都是輸出‘守候吃火鍋’。可是萬一我如今吃飽了,準備寫代碼了。這下怎麼實現呢?看代碼閉包

//面向對象
shouhou.coding=function(){
    console.log(this.name+'寫代碼');
}
shouhou.coding();
//面向過程
let coding=function(who){
    console.log(who+'寫代碼');
}
coding('守候');複製代碼

結果也同樣:‘守候寫代碼’

可是不難發現面向對象更加的靈活,複用性和擴展性更加。由於面向對象就是針對對象(例子中的:‘守候’)來進行執行某些動做。這些動做能夠自定義擴展。
而面向過程是定義不少的動做,來指定誰來執行這個動做。

好了,面向對象的簡單說明就到這裏了,至於面向對象的三大特性:繼承,封裝,多態這個自行上網查找資料。

3.this

使用 JavaScript 開發的時候,不少開發者多多少少會被 this 的指向搞蒙圈,可是實際上,關於 this 的指向,記住最核心的一句話:哪一個對象調用函數,函數裏面的this指向哪一個對象。

下面分幾種狀況談論下

3-1.普通函數調用

這個狀況沒特殊意外,就是指向全局對象-window。

let username='守候'
function fn(){
    alert(this.username);//undefined
}
fn();複製代碼

可能你們會困惑,爲何不是輸出守候,可是在細看一看,我聲明的方式是let,不會是window對象
若是輸出守候,要這樣寫

var username='守候'
function fn(){
    alert(this.username);//守候
}
fu();
//---------------
window.username='守候'
function fn(){
    alert(this.username);//守候
}
fn();
//能夠理解爲
//window.fn();複製代碼

3-2.對象函數調用

這個相信不難理解,就是那個函數調用,this指向哪裏

window.b=2222
let obj={
    a:111,
    fn:function(){
        alert(this.a);//111
        alert(this.b);//undefined
    }
}
obj.fn();複製代碼

很明顯,第一次就是輸出obj.a,就是111。而第二次,obj沒有b這個屬性,因此輸出undefined,由於this指向obj

可是下面這個狀況得注意

let obj1={
    a:222
};
let obj2={
    a:111,
    fn:function(){ alert(this.a);
    }
}
obj1.fn=obj2.fn;
obj1.fn();//222複製代碼

這個相信也不難理解,雖然obj1.fn是從obj2.fn賦值而來,可是調用函數的是obj1,因此this指向obj1

3-3.構造函數調用

let TestClass=function(){
    this.name='111';
}
let subClass=new TestClass();
subClass.name='守候';
console.log(subClass.name);//守候
let subClass1=new TestClass();
console.log(subClass1.name)//111複製代碼

這個也是不難理解,回憶下(new的四個步驟)就差很少了!

可是有一個坑,雖然通常不會出現,可是有必要提一下。

在構造函數裏面返回一個對象,會直接返回這個對象,而不是執行構造函數後建立的對象

3-4.apply和call調用

apply和call簡單來講就是會改變傳入函數的this。

let obj1={
    a:222
};
let obj2={
    a:111,
    fn:function(){ alert(this.a);
    }
}
obj2.fn.call(obj1);複製代碼

此時雖然是 obj2 調用方法,可是使用 了call,動態的把 this 指向到 obj1。至關於這個 obj2.fn 這個執行環境是 obj1applycall 詳細內容在下面說起。

3-5.箭頭函數調用

首先不得不說,ES6 提供了箭頭函數,增長了咱們的開發效率,可是在箭頭函數裏面,沒有 this ,箭頭函數裏面的 this 是繼承外面的環境。

一個例子

let obj={
    a:222,
    fn:function(){    
        setTimeout(function(){console.log(this.a)})
    }
};
obj.fn();//undefined複製代碼

不難發現,雖然 fn() 裏面的 this 是指向 obj ,可是,傳給 setTimeout 的是普通函數, this 指向是 windowwindow 下面沒有 a ,因此這裏輸出 undefined

換成箭頭函數

let obj={
    a:222,
    fn:function(){    
        setTimeout(()=>{console.log(this.a)});
    }
};
obj.fn();//222複製代碼

此次輸出 222 是由於,傳給 setTimeout 的是箭頭函數,而後箭頭函數裏面沒有 this ,因此要向上層做用域查找,在這個例子上, setTimeout 的上層做用域是 fn。而 fn 裏面的 this 指向 obj ,因此 setTimeout 裏面的箭頭函數的 this ,指向 obj 。因此輸出 222

4.call和apply

callapply 的做用,徹底同樣,惟一的區別就是在參數上面。
call 接收的參數不固定,第一個參數是函數體內 this 的指向,第二個參數如下是依次傳入的參數。
apply接收兩個參數,第一個參數也是函數體內 this 的指向。第二個參數是一個集合對象(數組或者類數組)

let fn=function(a,b,c){
console.log(a,b,c);
}
let arr=[1,2,3];複製代碼

如上面這個例子

let obj1={
    a:222
};
let obj2={
    a:111,
    fn:function(){ alert(this.a);
    }
}
obj2.fn.call(obj1);複製代碼

callapply 兩個主要用途就是

1.改變 this 的指向(把 thisobj2 指向到 obj1

2.方法借用( obj1 沒有 fn ,只是借用 obj2 方法)

5.閉包

閉包這個可能你們是迷糊,可是必需要征服的概念!下面用一個例子簡單說下

let add=(function(){
let now=0;
return {
 doAdd:function(){
    now++;
    console.log(now);
}
}
})()複製代碼

而後執行幾回!

上圖結果看到,now 這個變量,並無隨着函數的執行完畢而被回收,而是繼續保存在內存裏面。
具體緣由說下:剛開始進來,由於是自動執行函數,一開始進來會自動執行,這一塊

而後把這個對象賦值給 add 。因爲 add 裏面有函數是依賴於 now 這個變量。因此 now不會被銷燬,回收。這就是閉包的用途之一(延續變量週期)。因爲 now 在外面訪問不到,這就是閉包的另外一個用途(建立局部變量,保護局部變量不會被訪問和修改)。

可能有人會有疑問,閉包會形成內存泄漏。可是你們想下,上面的例子,若是不用閉包,就要用全局變量。把變量放在閉包裏面和放在全局變量裏面,影響是一致的。使用閉包又能夠減小全局變量,因此上面的例子閉包更好!

6.小結

在學設計模式的時候,遇到的知識點就是這一些了,這些知識點,也是我在羣聊,社區裏面,讓人掉坑比較多的考點。這些知識,能夠說是開發經常使用,面試常考的知識,仍是建議你們深刻些學習。上面那裏也是簡單的過一下而已。不算深刻。若是你們對文章有什麼建議,歡迎指點。

-------------------------華麗的分割線--------------------
想了解更多,關注關注個人微信公衆號:守候書閣

clipboard.png

相關文章
相關標籤/搜索