相對於普通函數的區別
新的書寫方式
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
下例中,
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
