1. 程式人生 > >ES6之箭頭函式深入理解

ES6之箭頭函式深入理解

相對於普通函式的區別

新的書寫方式 

this 的改變

不能當建構函式

沒有 prototype 屬性

沒有 arguments 物件

 

新的書寫方式

書寫方式很簡單!直接看下圖,

常規方式寫一個函式
const fun = function(number){
    return number * 2
}
  使用箭頭函式
const fun = (number) => {
    return number * 2
}
  如果只有一個引數,還可以省略前面的小括號
const fun = number => {
    return number * 2
}
  如果只有一條執行語句,甚至可以省略後面的大括號,而且可以也不能寫 return
const fun = number => number * 2 
  也可以寫成立即執行函式
const fun = (() => 3 * 2)()  // 6

 

this的改變

執行上下文

討論箭頭函式的 this 之前,不得不再熟悉一下 執行上下文(Execution Context),因為 this 指標(this value) 就儲存在執行上下文中。

執行上下文儲存著函式執行所需的重要資訊,其中有三個屬性:變數物件(variable object),作用域鏈(scope chain),this指標(this value),它們影響著變數的解析、變數作用域、函式 this 的指向。執行上下文分為 全域性執行上下文 和 函式執行上下文。

全域性程式碼開始執行前,會以 window 為目標產生一個全域性執行上下文, 開始對程式碼預編譯,這時候 this 指向的就是 window,接著開始執行全域性程式碼。

當函式程式碼開始執行前,會以函式為目標產生一個函式執行上下文,開始對該函式預編譯,這時候 this 的指向要分幾種情況(下面討論),接著開始執行函式程式碼,函式程式碼執行完後函式執行上下文就被會刪除。多個函式會產生多個執行上下文。

上面說到函式預編譯的 this 分下面四種情況:

第一種: 函式自主呼叫 如 fun() 或者是 普通的立即執行函式(區別於箭頭函式方式的立即執行函式), this 指向 window;

第二種: 函式被呼叫,當函式被某個物件呼叫時,函式產生的執行上下文中的 this 指向 該物件;

第三種: 通過 call() apply() bind() 方法改變 this,this 指向被傳入的 第一個引數;

第四種: 在建構函式中,this 指向被建立的 例項

由於箭頭函式是不能通過 call() apply() bind() 方法改變 this,也不能當做建構函式,所以接下來只演示第一和第二種情況的程式碼

 

var a = {
    origin: 'a',
    b: {
        origin: 'b',
        show: function(){
          var origin = 'show';
            console.log(this.origin);
       }
    }
}
var origin = 'window'
a.b.show();      // 因為 b 物件呼叫了 show 函式,所以 show 函式的執行上下文中的 this 指標指向 b 物件
var fun = a.b.show;     // 注意這裡是將 show 函式賦值給fun,相當於 var fun = function(){console.log(this)}
fun();      // 因為 fun 是自主呼叫,所以 this 指標指向 window,自然就列印 window 物件了

可能有人會有這個疑惑:a.b.show() 中,a 呼叫了 b,是不是 b 的 this 指向 a 了?

前面也說到了,this 儲存在執行上下文中,而只有 全域性 和 函式 才會產生執行上下文,在執行上下文裡記錄著 this,而 b 是全域性中 a 物件裡面的一個物件,不存在誰呼叫它,它的 this 就是誰的說法。

接下來理解箭頭函式中的 this 就非常容易了。

箭頭函式中的 this

首先,箭頭函式不會建立自己的 this,它只會從自己的作用域鏈上找父級執行上下文的 this,而不是誰呼叫它,它的 this 就是誰。所以箭頭函式中的 this,取決於你上層執行上下文的 this 。

下例中,

obj 分別呼叫了 show1 和 show2 兩個方法,所以show1 和 show2 中的 this 都是指向 obj,

show1 中, setTimeout 裡面是箭頭函式,從作用域鏈中找到 show1 中的 this,所以它的 this 就是 obj 物件;

show2 中,setTimeout 裡面的函式換成普通函式,函式自主呼叫,所以他的 this 就是 window 物件

var id = 0;
var obj = {
    id: 1,
    show1: function(){
        setTimeout(() => {
            console.log(this.id)
        }, 1000)
    },

  show2: function(){
    setTimeout(function(){
      console.log(this.id)
    }, 2000)
  }
}

obj.show1();    // 列印 1
obj.show2();    // 列印 0

 

不能當成建構函式

var Foo = () => {};
var foo = new Foo(); // TypeError: Foo is not a constructor

 

沒有 prototype 屬性

var Foo = () => {};
console.log(Foo.prototype); // undefined

 

沒有 arguments 物件

在大多數情況下,使用' ... ' 運算子是比使用 arguments 物件的更好選擇。

function foo(...arg) { 
  return arg; 
}
foo(1, 2, 3, 4); // 1
function foo(...numbers) { 
    numbers.forEach((number)=>{
        console.log(number);
    })
} 
foo(1, 2, 3, 4);  // 1 2 3 4