ES6之箭頭函式深入理解
相對於普通函式的區別
新的書寫方式
this 的改變
不能當建構函式
沒有 prototype 屬性
沒有 arguments 物件
新的書寫方式
書寫方式很簡單!直接看下圖,
常規方式寫一個函式const fun = function(number){ return number * 2 }使用箭頭函式
const fun = (number) => { return number * 2 }如果只有一個引數,還可以省略前面的小括號
const fun = number => { return number * 2 }
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