天天看點

ES6箭頭函數與普通函數的差別箭頭函數與普通函數的差別

箭頭函數與普通函數的差別

作為ES6中新加入的箭頭函數文法,深受廣大開發人員的喜愛,也是平時前端面試過程中經常會被提及問道的典型題目。它不僅簡化了我們的代碼,而且也讓開發人員擺脫了“飄忽不定”的this指向,本文就箭頭函數與普通函數的差別進行一些分析。

在我看來,面試官最關注的也是兩者最關鍵的差別就是this指向的差別,普通函數中的this指向函數被調用的對象,是以對于不同的調用者,this的值是不同的。而箭頭函數中并沒有自己的this(同時,箭頭函數中也沒有其他的局部變量,如this,argument,super等),是以箭頭函數中的this是固定的,它指向定義該函數時所在的對象。

普通函數

相信大家對普通函數的用法已經非常熟悉了,下面我們舉一個簡單的例子。

var a  = 3;
var obj = {
    a : 1,
    foo : function(){
        console.log(this.a);
    }
}
obj.foo(); //1
var bar = obj;
bar.a = 2;
bar.foo(); //2
var baz = obj.foo;
baz(); //3
           

上述代碼中,出現了三種情況:

  1. 直接通過obj調用其中的方法foo,此時,this就會指向調用foo函數的對象,也就是obj;
  2. 将obj對象賦給一個新的對象bar,此時通過bar調用foo函數,this的值就會指向調用者bar;
  3. 将obj.foo賦給一個新對象baz,通過baz()調用foo函數,此時的this指向window;

由此我們可以得出結論:

  • 普通函數的this總是指向它的直接調用者。
  • 在嚴格模式下,沒找到直接調用者,則函數中的this是undefined。
  • 在預設模式下(非嚴格模式),沒找到直接調用者,則函數中的this指向window。

再考慮一下下面的情況:

var obj = {
    a : 1,
    foo : function(){
        setTimeout(
            function(){console.log(this.a),3000})
    }
}
obj.foo(); //undefined
           

你可能會認為此時的輸出應該為1,但是結果卻是undefined。因為此時this的指向是全局的window對象。

通過以上例子,可以得出以下總結:

  • 對于方法(即通過對象調用了該函數),普通函數中的this總是指向它的調用者。
  • 對于一般函數,this指向全局變量(非嚴格模式下)或者undefined(嚴格模式下)。在上例中setTimeout中的function未被任何對象調用,是以它的this指向還是window對象。是以,這也可以總結成:javascript 的this 可以簡單的認為是後期綁定,沒有地方綁定的時候,預設綁定window或undefined。

如果我們希望可以在上例的setTimeout函數中使用this要怎麼做呢?在箭頭函數出現之前,我們往往會使用以下兩種方法:

  1. 在setTimeout函數的外部,也就是上層函數foo内部通過将this值賦給一個臨時變量來實作。
var obj = {
    a : 1,
    foo : function(){
        var that  = this;
        setTimeout(
            function(){console.log(that.a),3000})
    }
}
obj.foo(); //1
           
  1. 通過bind()來綁定this。
var obj = {
    a : 1,
    foo : function(){
        setTimeout(
            function(){console.log(this.a),3000}.bind(this))
    }
}
obj.foo(); //1
           

這種現象在ES6引入箭頭函數後得到了改善。

箭頭函數

箭頭函數是ES6中引入的新特性,使用方法為:

()=>{console.log(this)}
           

其中()内是要帶入的參數,{}内是要執行的語句。箭頭函數是函數式程式設計的一種展現,函數式程式設計将更多的關注點放在輸入和輸出的關系,省去了過程的一些因素,是以箭頭函數中沒有自己的this,arguments,new target(ES6)和 super(ES6)。箭頭函數相當于匿名函數,是以不能使用new來作為構造函數使用。

箭頭函數中的this始終指向其父級作用域中的this。換句話說,箭頭函數會捕獲其所在的上下文的this值,作為自己的this值。任何方法都改變不了其指向,如call(), bind(), apply()。在箭頭函數中調用 this 時,僅僅是簡單的沿着作用域鍊向上尋找,找到最近的一個 this 拿來使用,它與調用時的上下文無關。我們用代碼來解釋一下。

var obj = {
    a: 10,
    b: () => {
      console.log(this.a); // undefined
      console.log(this); // Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
    },
    c: function() {
      console.log(this.a); // 10
      console.log(this); // {a: 10, b: ƒ, c: ƒ}
    },
    d:function(){
        return ()=>{
            console.log(this.a); // 10
        }
    },
    e:function(){
      setTime
    }

  }
  obj.b(); 
  obj.c();
  obj.d()();
           

簡單分析一下代碼,obj.b()中的this會繼承父級上下文中的this值,也就是與obj有相同的this指向,為全局變量window。obj.c()的this指向即為調用者obj,obj.d().()中的this也繼承自父級上下文中的this,即d的this指向,也就是obj。

通過這個例子,也就可以大概的讓我們了解普通函數中的this和匿名函數中的this指向差别,進而更好的在工作中根據我們的需求正确合理地使用這兩種函數。

連結:https://www.jianshu.com/p/e5fe25edd78a

es6