更多文章詳見公眾號【前端css和js乾貨】
在 JavaScript 中,this是函數的調用上下文。
難度在於this具有複雜的行為。
本文,整理了一個關於this關鍵字的7個有趣面試問題的列表。
注意:下面的 JavaScript 片段在非嚴格模式下運行,也稱為 sloppy 模式。
1: 變量 vs 屬性
以下代碼會在控制枱打印什麼:
const object = {
message: 'Hello, World!',
getMessage() {
const message = 'Hello, Earth!';
return this.message;
}
};
console.log(object.getMessage()); // 會打印什麼?
<details>
<summary>點擊查看答案</summary>
'Hello, World!' 被打印到控制枱。
object.getMessage() 是一個方法調用,所以this指向object。
方法裏面還有一個變量聲明 const message = 'Hello, Earth!'。 該變量並不會影響this.message的值。
</details>
2: Cat name
function Pet(name) {
this.name = name;
this.getName = () => this.name;
}
const cat = new Pet('Fluffy');
console.log(cat.getName()); // 會打印什麼?
const { getName } = cat;
console.log(getName()); // 會打印什麼?
<details>
<summary>點擊查看答案</summary>
控制枱會打印出'Fluffy' 和 'Fluffy'。
當一個函數作為構造函數調用 new Pet('Fluffy') 時,構造函數內部的 this 等於構造的對象。
Pet構造函數中的 this.name = name 代碼會在構造的對象上創建 name 屬性。
this.getName = () => this.name 在構造的對象上創建一個方法 getName 。 並且由於使用了箭頭函數,所以箭頭函數內部的 this 就等於外部作用域的 this——構造函數 Pet。
調用 cat.getName() 和 getName(),返回的結果都是為“Fluffy”。
</details>
3.setTimeout和this
const object = {
message: 'Hello, World!',
logMessage() {
console.log(this.message); // 會打印什麼?
}
};
setTimeout(object.logMessage, 1000);
<details>
<summary>點擊查看答案</summary>
延遲 1 秒後, undefined 會打印到控制枱。
雖然 setTimeout() 函數使用 object.logMessage 作為回調,但它會將 object.logMessage 作為常規函數而不是方法。
並且在常規函數調用期間,this指向全局對象,在瀏覽器環境的情況下是window。
這就是 logMessage 方法中的 console.log(this.message) 打印undefined的 的原因。
</details>
附帶挑戰:如何修復此代碼以便'Hello,World!' 打印到控制枱?
4.人工方法
如何調用 logMessage 函數才能使他打印出“Hello, World!”?
const object = {
message: 'Hello, World!'
};
function logMessage() {
console.log(this.message); // "Hello, World!"
}
// 這裏寫下你的代碼...
<details>
<summary>點擊查看答案</summary>
至少有 3 種方法可以對象的方法調用 logMessage() 。
const object = {
message: 'Hello, World!'
};
function logMessage() {
console.log(this.message); // logs 'Hello, World!'
}
// 使用 func.call()
logMessage.call(object);
// 使用 func.apply()
logMessage.apply(object);
// Creating a bound function
const boundLogMessage = logMessage.bind(object);
boundLogMessage();
</details>
5.箭頭函數
const object = {
who: 'World',
greet() {
return `Hello, ${this.who}!`;
},
farewell: () => {
return `Goodbye, ${this.who}!`;
}
};
console.log(object.greet()); // 會打印什麼?
console.log(object.farewell()); // 會打印什麼?
<details>
<summary>點擊查看答案</summary>
'Hello, World!' 和 'Goodbye, undefined!' 會被打印到控制枱。
當調用 object.greet() 時,在方法greet() 中,因為greet 是一個常規函數,this指向object。 因此 object.greet() 返回 'Hello, World!'。
但是farewell() 是一個箭頭函數,所以箭頭函數內部的this總是等同於外部作用域的this。
farewell() 的外部作用域是全局作用域,這裏 this 是全局對象。 因此 object.farewell() 實際上返回“Goodbye, ${window.who}!”,其計算結果為“Goodbye, undefined!”。
</details>
6.困惑的length
以下代碼會打印什麼?
var length = 4;
function callback() {
console.log(this.length); // What is logged?
}
const object = {
length: 5,
method(callback) {
callback();
}
};
object.method(callback, 1, 2);
<details>
<summary>點擊查看答案</summary>
4會被打印到控制枱中。
callback() 是以常規函數方式來調用的。 由於在常規函數調用時this指向全局對象,因此在callback()函數內的this.length 被評估為 window.length。
在最外層的作用域中,第一條語句 var length = 4,在全局對象上創建了一個屬性 length:window.length 變為 4。
最後,在 callback() 函數中 this.length 計算為 window.length ,所以4 被記錄到控制枱。
</details>
7.調用參數
var length = 4;
function callback() {
console.log(this.length); // What is logged?
}
const object = {
length: 5,
method() {
arguments[0]();
}
};
object.method(callback, 1, 2);
<details>
<summary>點擊查看答案</summary>
3 會被打印到控制枱。
obj.method(callback, 1, 2) 使用 3 個參數調用:callback, 1 和 2。因此,method() 中的arguments變量是以下結構的類數組對象:
{
0: callback,
1: 1,
2: 2,
length: 3
}
因為arguments[0]() 是arguments對象的callback方法的調用,所以回調中的this指向arguments。 因此 callback() 中的 this.length 與 arguments.length 相同,即 3。
</details>
總結
如果您正確回答了 5 個或更多問題,那麼您就對this關鍵字有了很好的理解!
否則,您需要好好複習一下this關鍵字。
作者:Dmitri Pavlutin 譯者:前端css和js乾貨 來源:dmitripavlutin