JavaScript 101
全球的this
this
在常规函数内部
this
箭头函数内部
更多阅读材料:
this
是最常见的 JS 关键字之一。你随处可见它们,但很难分辨它们this
是什么。
我将介绍三种可能使用的场景this
:全局、常规函数内部以及箭头函数内部。这应该涵盖了大多数用法。
- 全球的
this
this
在常规函数内部this
箭头函数内部
让我们先看一些例子!
顺便说一下,我将在浏览器(Chrome)控制台中执行此操作,而不是在 Node 模块中。我还假设未使用严格模式。
全球的this
如果我们只是this
在浏览器控制台中输入,它将引用窗口/全局对象。
this // Window {...}
var helloVar = 'helloVar'
this.helloVar // helloVar
window.helloWindow = 'helloWindow'
this.helloWindow // 'helloWindow'
const helloConst = 'helloConst'
this.helloConst // undefined
let helloLet = 'helloLet'
this.helloLet // undefined
你会看到,let
和const
无法通过 调用this
。它们不是存储在“对象环境记录”中,而是存储在“声明性环境记录”中。解释这一点超出了本文的范围。如果你感兴趣,可以点击这里查看链接。
this
在常规函数内部
让我们从一个例子开始:
const obj = {
breakfast: 'donut',
wutBreakfast: function() {console.log(`I had ${this.breakfast} this morning!`)}
}
window.breakfast = 'waffles';
obj.wutBreakfast() // I had donut this morning!
这里我们观察到this
insidethis.breakfast
指的是对象本身。查看调用 时函数调用的位置obj.wutBreakfast()
。问问自己:“我的函数调用左边是否有一个对象?” 那个对象就是 yourthis
所指的位置。
如果函数调用左侧没有对象怎么办?如果你调用一个函数,而函数调用左侧没有对象,你可以假设它是全局对象。在本例中,就是这个Window
对象。
我们来看下一个例子:
function sayBrunch(){
console.log(`I had ${this.brunch} for brunch!`)
}
sayBrunch() // I had undefined for brunch
我们还没有为 brunch 定义任何内容,所以它返回 undefined。让我们在 window 对象中定义它。
window.brunch = 'oatmeal'
function sayBrunch(){
console.log(`I had ${this.brunch} for brunch!`)
}
sayBrunch() // I had oatmeal for brunch!
让我们再举几个例子来建立你的直觉:
window.dinner = 'pizza'
const foodObj = {
dinner: 'spaghetti',
sayDinner: function(){
console.log(`I had ${this.dinner} for dinner!`)
}
}
foodObj.sayDinner() // what does it return?
再来一个,稍微有点变化。我们定义了一个 window appetizer 字符串和一个 mealObj.appetizer 字符串。我们从两个不同的对象调用 sayAppetizers。你觉得它们会返回什么呢?
window.appetizer = 'chocolate';
function sayAppetizer(){
console.log(`I had ${this.appetizer} for appetizer!`)
}
const mealObj = {
appetizer: 'ice cream',
sayAppetizer: sayAppetizer
}
mealObj.sayAppetizer() // what does it return?
sayAppetizer() // what does it return?
请记住,this
在常规 JS 中,函数引用的是调用函数时紧邻左侧的对象。如果没有对象,则假定它是一个窗口对象。
考虑到这一点,即使我们有obj1.obj2.obj3.someFunc()
,我们也知道this
insidesomeFunc()
将引用,obj3
因为它是距离函数被调用位置最近的对象。
this
箭头函数内部
这在箭头函数中的行为有所不同。你需要始终牢记三件事:
- 只有常规函数和全局函数才可以有
this
。 - 箭头函数本身不具有
this
- 当
this
箭头函数内部引用 时,它会查找作用域来找到 this 值。其行为类似于词法作用域。
让我们看第一个例子:
let myObj = {
breakfast: 'taco',
sayBreakfast: () => {
console.log(`I had ${this.breakfast} for breakfast`)
}
}
window.breakfast = 'pizza'
myObj.sayBreakfast() // pizza
让我们看看在牢记上述三条规则的情况下,这是否合理:
当我们调用 myObj.sayBreakfast() 时,它会查找 myObj,但由于 myObj没有this
(规则 2),它会再查找一个,即 global/window 对象(规则 1)。它发现 global/window 有this.breakfast = 'pizza'
,所以打印了 pizza。
现在向对象添加一个常规函数:
let myObj = {
breakfast: 'taco',
sayBreakfast: () => {
console.log(`I had ${this.breakfast} for breakfast`)
},
sayRegBreakfast: function() {
console.log(`I had ${this.breakfast} and it was yummy`)
}
}
window.breakfast = 'pizza'
myObj.sayBreakfast() // pizza
myObj.sayRegBreakfast() // taco
您会看到,使用常规函数会给出“taco”,而使用箭头会给出“pizza”。
让我们从全局对象作用域调用一个箭头函数。我们应该预期它来自this
全局作用域。是这样吗?
window.secondBreakfast = 'eggs';
const saySecondBreakfast = () => {
console.log(`I had ${this.secondBreakfast} for second breakfast!`)
}
saySecondBreakfast() // eggs
我当时看到这个也难以置信,所以让我们进一步证明一下。下面的例子来自getify 存档:
function foo() {
return function() {
return function() {
return function() {
console.log("Id: ", this.id);
}
}
}
}
foo.call( { id: 42} )()()() // undefined
对比
function foo2() {
return () => {
return () => {
return () => {
console.log("id:", this.id);
};
};
};
}
foo2.call( { id: 42 } )()()() // 42
(顺便说一下,call分配this
给我们正在调用的函数 - foo/foo2 本身 - 以及我们传递的参数对象)
请记住,只有箭头函数按词汇方式向上查找;第一个例子在第三个this
嵌套函数内部查找,但没有找到任何内容,因此返回未定义。
而 foo2 在第三个嵌套函数中找不到this
,于是在词法上查找下一个可用的 reg/全局函数this
。它首先找到了 foo2 的this
(来自foo2.call({id: 42})
)(记住规则 1),因此打印 42。
如果前面第二个例子中有一个常规函数,它就不会找到它,例如:
function foo3() {
return () => {
return function() { // this is regular function now
return () => {
console.log("id:", this.id);
};
};
};
}
foo3.call({id:101})()()() // undefined
但是如果我们传入的this
位置return function() {...})
,它就能找到它。因为当箭头函数按词法查找并找到第一个常规函数时,该函数会被赋值this
为 101。
function foo3() {
return () => {
return function() {
return () => {
console.log("id:", this.id);
};
};
};
}
foo3()().call({id: 101})() // 101
就这样吧,伙计们!这肯定只是冰山一角,但this
应该足够你入门了——双关语😁。
如果您有任何问题或发现错误请告诉我 - 感谢您的阅读并祝您编码愉快!!