最近翻譯了一篇關於 this 的文章,很多人評價不錯,但是原文沒有講到箭頭函式的 this
,所以我來補充一篇文章來專門講解。
箭頭函式是 ES6 新增的新語法,基礎知識就不多介紹了,下面我們來講一講箭頭函式的 this
怎麼指向。
問題起源
在以往的函式中,this
有各種各樣的指向(隱式繫結,顯示繫結,new 繫結, window 繫結......),雖然靈活方便,但由於不能在定義函式時而直到實際呼叫時才能知道 this
指向,很容易給開發者帶來諸多困擾。
假如我們有下面這段程式碼(本文程式碼都是在瀏覽器下執行),
function User() {
this.name = 'John';
setTimeout(function greet() {
console.log(`Hello, my name is ${this.name}`); // Hello, my name is
console.log(this); // window
}, 1000);
}
const user = new User();
複製程式碼
greet
裡的 this
可以由上一篇文章的四個規則判斷出來。對,因為沒有顯示繫結、隱式繫結或 new
繫結、所以直接得出結論 this
指向 window
。但實際上我們想把 this
指向 user
物件!
以前是怎麼解決的呢?看下面的程式碼:
1. 使用閉包
function User() {
const self = this;
this.name = 'John';
setTimeout(function greet() {
console.log(`Hello, my name is ${self.name}`); // Hello, my name is John
console.log(self); // User {name: "John"}
}, 1000);
}
const user = new User();
複製程式碼
2. 使用顯示繫結 — bind
function User() {
this.name = 'John';
setTimeout(function greet() {
console.log(`Hello, my name is ${this.name}`); // Hello, my name is John
console.log(this); // User {name: "John"}
}.bind(this)(), 1000);
}
const user = new User();
複製程式碼
3. 利用 setTimeout
的可以傳更多引數的特性
其實第三種和第一種比較像,都用到了閉包。
function User() {
this.name = 'John';
setTimeout(function greet(self) {
console.log(`Hello, my name is ${self.name}`); // Hello, my name is John
console.log(self); // User {name: "John"}
}, 1000, this);
}
const user = new User();
複製程式碼
三種方法都可以解決問題,但是都要額外寫冗餘的程式碼來指定 this
。
現在,箭頭函式(Arrow Function)正是 ES6 引入來解決這個問題的,它可以輕鬆地讓 greet
函式保持 this
指向 user
物件。
箭頭函式如何解決
下面是箭頭函式版本:
function User() {
this.name = 'John';
setTimeout(() => {
console.log(`Hello, my name is ${this.name}`); // Hello, my name is John
console.log(this); // User {name: "John"}
}, 1000);
}
const user = new User();
複製程式碼
完美,直接把普通函式改成箭頭函式就能解決問題。
箭頭函式在自己的作用域內不繫結 this
,即沒有自己的 this
,如果要使用 this
,就會指向定義時所在的作用域的 this
值。在上面的程式碼中即指向 User
函式的 this
,而 User 函式通過 new
繫結,所以 this
實際指向 user
物件。
如果上述程式碼在嚴格模式下執行會有影響嗎?
function User() {
this.name = 'John';
setTimeout(() => {
'use strict'
console.log(`Hello, my name is ${this.name}`); // Hello, my name is John
console.log(this); // User {name: "John"}
}, 1000);
}
const user = new User();
複製程式碼
答案是沒有影響。因為箭頭函式沒有自己的 this
,它的 this
來自於 User
的 this
,只要 User
的 this
不變,箭頭函式的 this
也保持不變。
那麼使用 bind
,call
或者 apply
呢?
function User() {
this.name = 'John';
setTimeout((() => {
console.log(`Hello, my name is ${this.name}`); // Hello, my name is John
console.log(this); // User {name: "John"}
}).bind('no body'), 1000);
}
const user = new User();
複製程式碼
答案還是沒有影響。因為箭頭函式沒有自己的 this
,使用 bind
,call
或者 apply
時,箭頭函式會自動忽略掉 bind
的第一個引數,即 thisArg
。
總結:箭頭函式在自己的作用域內沒有自己的 this
,如果要使用 this
,就會指向定義時所在的作用域的 this
值。
歡迎在下方評論交流哦!