参加“this”测验,了解“this”在 JavaScript 中的工作原理
在 JavaScript 提供的所有抽象概念中,“this”关键字可能是最难掌握的概念之一。从表面上看,“this”似乎是一个直观的关键字,仅仅指代它所处的环境(或上下文)。
当您深入了解 JavaScript 运行时(即代码执行的位置)时,“this”关键字最终可能会抓取您意想不到的东西。
在这篇文章中,我创建了 4 个简单的场景,其中“this”关键字可以有不同的解释,每个场景后面跟着一个多项选择部分、一个长时间的停顿(以防你滚动得太快而意外看到答案)以及带有解释的答案。
您可以在控制台或文本编辑器上随意尝试代码。记住,您遇到的情况越多、越多样化,您就越能识别和理解“this”关键字。
准备好了吗?我们开始吧!
挑战#1
const call = {
caller: "mom",
says: function() {
console.log(`Hey, ${this.caller} just called.`);
}
};
call.says();
上面的代码将在控制台上输出什么?
(A)嘿,undefined 刚打来电话。
(B)嘿,妈妈刚打来电话。
(C)嘿,来电者刚打来电话。
...
...
...
...
...
...
...
...
...
...
答案是……
(B) 嘿,妈妈刚刚打电话来。
代码块如下:
const call = {
caller: "mom",
says: function() {
console.log(`Hey, ${this.caller} just called.`);
}
};
call.says();
这里我们在 call 对象内部有一个函数声明。一般来说,“this”由调用函数的对象决定。因此,当 call 对象调用 says 函数 (call.says()) 时, says 函数内部的“this”关键字指向 call 对象,因此 this.caller 等于 “mom”。
很简单,对吧?
挑战#2
const call = {
caller: "mom",
says: () => {
console.log(`Hey, ${this.caller} just called.`);
}
};
call.says();
上面的代码将在控制台上输出什么?
(A)嘿,undefined 刚打来电话。
(B)嘿,妈妈刚打来电话。
(C)嘿,来电者刚打来电话。
...
...
...
...
...
...
...
...
...
...
答案是……
(A) 嘿,undefined 刚刚打来电话。
代码块如下:
const call = {
caller: "mom",
says: () => {
console.log(`Hey, ${this.caller} just called.`);
}
};
call.says();
等等,这个代码不是和第一个一样吗?
如果仔细观察,就会发现挑战#1 中的函数声明现在已被箭头函数取代。
作为 ES6 语法的一部分,箭头函数没有自己的this关键字。相反,它们会使用函数创建时外部this的this关键字。
换句话说,箭头函数内的“this”并未绑定到我们的调用对象,而是已经绑定到最初创建调用对象的位置,在本例中是全局对象。
并且因为全局对象对 say() 函数一无所知,所以“this”是未定义的。而且由于全局对象没有 caller 属性,因此 this.caller 未定义。(感谢James Nylen的纠正!)
挑战#3
const call = {
caller: "mom",
says: function() {
console.log(`Hey, ${this.caller} just called.`);
}
};
let newCall = call.says;
newCall();
上面的代码将在控制台上输出什么?
(A) 嘿,undefined 刚刚打来电话。
(B) 嘿,妈妈刚刚打来电话。
...
...
...
...
...
...
...
...
...
...
答案是……
(A) 嘿,undefined 刚刚打来电话。
发生了什么?我们再看一下代码:
const call = {
caller: "mom",
says: function() {
console.log(`Hey, ${this.caller} just called.`);
}
};
let newCall = call.says;
newCall();
在这里,我们声明一个新变量,newCall
并将 call 对象中的 says 函数赋值给newCall
。然后我们调用newCall
,这是一个简单的函数调用。
注意我们在哪里调用函数。它在调用对象内部吗?不是。我们全局调用 newCall() 函数,这使得this关键字等于全局对象。
正如挑战#2 中所示,由于全局对象没有调用者属性,因此结果为“未定义”。
现在,您可能会注意到一个关键模式:
常规函数根据调用该函数的对象改变其行为。
挑战#4
function anotherCaller() {
console.log(`${this.caller} called, too!`);
}
const call = {
caller: "mom",
anotherCaller: anotherCaller,
says: function() {
console.log(`Hey, ${this.caller} just called.`);
}
};
let newCall = call.anotherCaller;
newCall();
上面的代码将在控制台中记录什么?
(A) 妈妈也打来电话了!
(B) 嘿,妈妈刚刚打来电话了。
(C) undefined 也打来电话了!
...
...
...
...
...
...
...
...
...
...
答案是……
(C) undefined 也打来电话!
再次注意函数被调用的位置:
function anotherCaller() {
console.log(`${this.caller} called, too!`);
}
const call = {
caller: "mom",
anotherCaller: anotherCaller,
says: function() {
console.log(`Hey, ${this.caller} just called.`);
}
};
let newCall = call.anotherCaller;
newCall();
我们正在全局调用 newCall() 函数,这意味着'this'关键字指向的是全局对象。将 newCall 赋值给调用对象内部的函数并不重要。我们正在全局调用 newCall,而'this' 的赋值位置就在全局。
如果您喜欢冒险,请尝试将 anotherCaller() 函数移到调用对象内部,如下所示:
const call = {
caller: "mom",
anotherCaller: function() {
console.log(`${this.caller} called, too!`)
},
says: function() {
console.log(`Hey, ${this.caller} just called.`);
}
};
let newCall = call.anotherCaller;
newCall();
根据我们刚才讨论的内容,您认为结果会是什么?
在浏览器中查看答案之前,先在心里默读一遍代码。如果你能理解,那你就掌握了(至少掌握了基础知识)!
我希望这些示例能让你更好地理解“this”关键字的工作原理。如果你仍然感到困惑,不用担心。编程的一切,实践才是关键。
更多示例,请查看MDN 官方文档中关于 this 的内容。我也强烈推荐这篇很棒的文章。作者的解释清晰,实际上让我对上次挑战中遇到的一些棘手部分有了更深入的了解。
文章来源:https://dev.to/liaowow/take-this-quiz-understand-how-this-works-in-javascript-44dj