我希望找到一个“this”关键字的明确解释,以及如何正确使用它。
它的行为似乎很奇怪,我不完全明白为什么。
这是如何工作的,何时使用?
我希望找到一个“this”关键字的明确解释,以及如何正确使用它。
它的行为似乎很奇怪,我不完全明白为什么。
这是如何工作的,何时使用?
当前回答
丹尼尔,太棒了!在事件处理程序的情况下,这里有几个单词,以及这个执行上下文指针的良好列表。
换句话说,JavaScript中的这一点指向运行当前函数的对象(或其执行上下文),并且它始终是只读的,无论如何都不能设置它(这样的尝试将以“赋值中的左侧无效”消息结束)。
对于事件处理程序:内联事件处理程序,例如<elementonclick=“foo”>,会覆盖之前附加的任何其他处理程序,因此要小心,最好不要进行内联事件委派。感谢Zara Alaverdyan,他通过一场不同意见的辩论激发了我的灵感:)
el.onclick=foo;//在foo-obj中el.onclick=函数(){this.style.color=“#fff”;}//objel.onclick=function(){doSomething();}//在doSometing中-窗el.addEventListener('click',foo,false)//在foo-obj中el.attachEvent('包含,函数(){//this}')//窗口,所有符合IE:)<button onclick=“this.style.color='#fff';”>//obj<button onclick=“foo”>//在foo窗口中,但您可以<buttononclick=“foo(this)”>
其他回答
关于如何在JavaScript中解释“this”关键字,存在很多困惑。希望这篇文章能让所有这些人一劳永逸。还有更多。请仔细阅读整篇文章。预先警告,这篇文章很长。
无论使用的上下文如何,“this”总是引用Javascript中的“当前对象”。然而,“当前对象”是什么根据上下文而不同。上下文可能正好是以下6项中的1项:
全局(即在所有功能之外)内部直接“非绑定函数”调用(即尚未通过调用functionName.bind绑定的函数)内部间接“非绑定函数”通过functionName.Call和functionName.apply调用在“绑定函数”内部调用(即通过调用functionName.bind绑定的函数)通过“新建”创建对象内联DOM事件处理程序内部
以下逐一介绍了每种情况:
全局上下文(即所有功能外部):在所有功能之外(即在全局上下文中)对象”(因此“this”的值)始终是浏览器的“窗口”对象。内部直接“非绑定函数”调用:在直接“非绑定函数”调用中调用的函数调用变为“当前对象”(因此“this”的值)。如果在没有显式当前对象的情况下调用函数,则当前对象要么是“窗口”对象(对于非严格模式),要么是未定义的(对于严格模式)。中定义的任何函数(或变量)全局上下文自动成为“窗口”对象的属性。例如,假设函数在全局上下文中定义为函数UserDefinedFunction(){警报(this)}它成为窗口对象的属性,就像您定义了它作为window.UserDefinedFunction=函数(){警报(this)} 在“非严格模式”下,直接通过“UserDefinedFunction()”调用/调用此函数将自动调用/调用它作为“window.UserDefinedFunction()”,将“window”作为“UserDefinedFunction”中的“当前对象”(以及“this”的值)。在“非严格模式”中调用此函数将导致以下结果UserDefinedFunction()//显示[object Window],因为它自动被调用为窗口。UserDefined Function()在“严格模式”下,通过“UserDefinedFunction()”将“NOT”自动将其调用为“window.UserDefinedFunctions()”。因此,“当前对象”(以及“this”的值)“UserDefinedFunction”应未定义。在“严格模式”下调用此函数将导致以下结果UserDefinedFunction()//显示未定义然而,使用窗口对象显式调用它将导致以下内容window.UserDefinedFunction()//“无论模式如何,始终显示[object window]。”让我们看看另一个例子。请查看以下代码函数UserDefinedFunction(){警报(this.a+“,”+this.b+“,“+this.c+”,“+this.d”)}变量o1={a: 1中,b: 2,f: 用户定义函数}无功氧气={c: 3中,d: 4中,f: 用户定义函数}o1.f()//应显示1,2,未定义,未定义o2.f()//应显示未定义、未定义、3,4在上面的示例中,我们看到当“UserDefinedFunction”通过o1调用,“this”取值为o1显示其财产“a”和“b”的值。价值“c”和“d”的定义与o1相同没有定义这些财产类似地,当通过o2调用“UserDefinedFunction”时,“this”取o2的值,并显示其财产“c”和“d”的值。“a”和“b”的值显示为未定义,因为o2未定义这些财产。间接“非绑定函数”内部通过functionName.Call和functionName.apply调用:当通过调用“非绑定函数”时functionName.call或functionName.apply,“当前对象”(因此“this”的值)设置为传递给调用/应用的“this”参数(第一个参数)。下面的代码也演示了这一点。函数UserDefinedFunction(){警报(this.a+“,”+this.b+“,“+this.c+”,“+this.d”)}变量o1={a: 1中,b: 2,f: 用户定义函数}无功氧气={c: 3中,d: 4中,f: 用户定义函数}UserDefinedFunction.call(o1)//应显示1,2,未定义,未定义UserDefinedFunction.apply(o1)//应显示1,2,未定义,未定义UserDefinedFunction.call(o2)//应显示未定义、未定义、3、4UserDefinedFunction.apply(o2)//应显示未定义、未定义、3、4o1.f.call(o2)//应显示未定义、未定义、3、4o1.f.apply(o2)//应显示未定义、未定义、3、4o2.f.call(o1)//应显示1,2,未定义,未定义o2.f.apply(o1)//应显示1,2,未定义,未定义上述代码清楚地表明,任何“NON”的“this”值绑定函数”可以通过调用/应用来更改。此外,如果“this”参数未显式传递给调用/应用,“current object”(因此“this”的值)在非严格模式下设置为“window”,在严格模式下为“undefined”。在“绑定函数”调用(即通过调用functionName.bind绑定的函数)内部:绑定函数是其“this”值为固定的下面的代码演示了“this”在有界函数的函数UserDefinedFunction(){警报(this.a+“,”+this.b+“,“+this.c+”,“+this.d”)}变量o1={a: 1中,b: 2,f: 用户定义函数,bf:空}无功氧气={c: 3中,d: 4中,f: 用户定义函数,bf:空}var bound1=用户定义函数.bind(o1);//将函数“bound1”的“this”值永久固定到对象o1bound1()//应显示1,2,未定义,未定义var bound2=UserDefinedFunction.bind(o2);//将函数“bound2”的“this”值永久固定到对象o2bound2()//应显示undefined,undefineed,3,4var边界3=o1.f.bind(o2);//将函数“bound3”的“this”值永久固定到对象o2bound3()//应显示undefined,undefineed,3,4var绑定4=o2.f.bind(o1);//永久地将函数“bound4”的“this”值固定到对象o1bound4()//应显示1,2,undefined,undefineo1.bf=UserDefinedFunction.bind(o2)//将函数“o1.bf”的“this”值永久固定到对象o2o1.bf()//应显示未定义、未定义、3,4o2.bf=UserDefinedFunction.bind(o1)//将函数“o2.bf”的“this”值永久固定到对象o1o2.bf()//应显示1,2,未定义,未定义bound1.call(o2)//仍应显示1,2,undefined,undefine。“call”不能更改绑定函数的“this”值bound1.apply(o2)//仍应显示1,2,undefined,undefine。“apply”不能更改绑定函数的“this”值o2.bf。
以下总结了整篇文章
在全局上下文中,“this”总是指“window”对象每当调用函数时,都会在对象(“当前对象”)。如果未明确提供当前对象,当前对象是NON Strict中的“窗口对象”默认情况下,模式和严格模式中的“未定义”。非绑定函数中“this”的值是对调用该函数的上下文中的对象(“当前对象”)的引用非绑定函数中“this”的值可以由调用和应用函数的方法。“this”的值对于Bound函数是固定的,不能被函数的调用和应用方法覆盖。绑定和已绑定函数不会更改“this”的值。它保持设置为第一个绑定函数设置的值。构造函数中“this”的值是已创建并初始化内联DOM事件处理程序中“this”的值是引用指定事件处理程序的元素。
JavaScript中的this总是指正在执行的函数的“所有者”。
如果未定义显式所有者,则引用最顶层的所有者窗口对象。
所以如果我做了
function someKindOfFunction() {
this.style = 'foo';
}
element.onclick=someKindOfFunction;
这将引用元素对象。但是要小心,很多人都会犯这个错误。
<element onclick=“someKindOfFunction()”>
在后一种情况下,您只是引用函数,而不是将其交给元素。因此,这将引用窗口对象。
这是我见过的最好的解释:清晰地理解JavaScripts
此引用总是指(并保持)object-一个单独的对象,通常用于函数或方法,尽管它可以在全局范围请注意,当我们使用strict模式时在全局函数和匿名函数中未定义绑定到任何对象。
有四种情况可能会令人困惑:
当我们传递一个方法(使用此方法)作为参数用作回调函数时。当我们使用内部函数(闭包)时。需要注意的是,闭包不能通过使用this关键字访问外部函数的this变量,因为this变量只能由函数本身访问,而不能由内部函数访问。当依赖于此的方法被分配给跨上下文的变量时,在这种情况下,它引用了另一个对象,而不是最初预期的对象。与bind、apply和call方法一起使用时。
他给出了代码示例、解释和解决方案,我认为这很有帮助。
这是JavaScript中的关键字,是执行上下文的属性。它的主要用途是函数和构造函数。这方面的规则非常简单(如果你坚持最佳实践)。
规范中的技术说明
ECMAScript标准通过抽象操作(缩写为AO)ResolveThisBinding对此进行了定义:
[AO]ResolveThisBinding[…]使用正在运行的执行上下文的LexicalEnvironment确定关键字this的绑定。[步骤]:让envRec为GetThisEnvironment()。回来envRec.GetThisBinding()。
全局环境记录、模块环境记录和函数环境记录都有自己的GetThisBinding方法。
GetThisEnvironment AO查找当前运行的执行上下文的LexicalEnvironments,并查找最接近的优势环境记录(通过迭代访问其[[OuterEnv]]财产),该记录具有此绑定(即HasThisBinding返回true)。此过程以三种环境记录类型之一结束。
这个值通常取决于代码是否处于严格模式。
GetThisBinding的返回值反映了当前执行上下文的this值,因此无论何时建立新的执行上下文,它都会解析为一个不同的值。修改当前执行上下文时也可能发生这种情况。以下小节列出了可能发生这种情况的五种情况。
您可以将代码样本放在AST资源管理器中,以遵循规范细节。
1.脚本中的全局执行上下文
这是在顶层评估的脚本代码,例如直接在<script>中:
<script>
// Global context
console.log(this); // Logs global object.
setTimeout(function(){
console.log("Not global context");
});
</script>
在脚本的初始全局执行上下文中,计算此值会导致GetThisBinding采取以下步骤:
全局环境记录envRec的GetThisBinding具体方法[…][执行此操作]:返回envRec。[[GlobalThisValue]]。
全局环境记录的[[GlobalThisValue]]属性始终设置为主机定义的全局对象,该对象可通过globalThis(Web上的窗口,Node.js上的全局;MDN上的文档)访问。按照InitializeHostDefinedRealm的步骤,了解[[GlobalThisValue]]属性是如何产生的。
2.模块中的全局执行上下文
ECMAScript 2015中引入了模块。
这适用于模块,例如直接在<script type=“module”>内部时,而不是简单的<script>。
当在模块的初始全局执行上下文中时,计算此值会导致GetThisBinding采取以下步骤:
模块Environment Record[…]的GetThisBinding具体方法[执行此操作]:返回未定义。
在模块中,this的值始终在全局上下文中未定义。模块隐式处于严格模式。
3.输入eval代码
有两种eval调用:直接调用和间接调用。这种区别自ECMAScript第5版以来就存在。
直接eval调用通常看起来像eval(…);或(eval)(…);(或((eval))(…);,1只有在调用表达式符合窄模式时才是直接的。2间接eval调用涉及以任何其他方式调用函数引用eval。可能是eval?。(…),(…,eval)(…)、window.eval(…)和eval.call(…,…)等。给定常量aliasVal1=eval;window.aliasVal2=eval;,它也可以是aliasVal1(…)、aliasVal2(…)。另外,给定const originalEval=eval;window.eval=(x)=>原始评估(x);,调用eval(…)也是间接的。
请参阅chuckj对“JavaScript中的(1,eval)('this')vs eval('this')?”和Dmitry Soshnikov的ECMA-262-5的详细回答–第2章:严格模式(存档),了解何时可以使用间接eval()调用。
PerformEval执行eval代码。它创建一个新的声明性环境记录作为其LexicalEnvironment,GetThisEnvironment从中获取此值。
然后,如果这出现在eval代码中,则调用GetThisEnvironment找到的环境记录的GetThisBinding方法并返回其值。
创建的声明性环境记录取决于eval调用是直接调用还是间接调用:
在直接求值中,它将基于当前运行的执行上下文的LexicalEnvironment。在间接求值中,它将基于执行间接求值的Realm Record的[[GlobalEnv]]属性(全局环境记录)。
这意味着:
在直接求值中,this值不变;它取自称为eval的词法范围。在间接求值中,this值是全局对象(globalThis)。
新功能如何? — newFunction类似于eval,但它不会立即调用代码;它创建了一个函数。此绑定不适用于此处的任何地方,除非调用函数时正常工作,如下一小节所述。
4.输入功能代码
调用函数时会输入函数代码。
调用函数有四类语法。
EvaluateCall AO针对以下三项进行:3正常函数调用可选链接调用标记的模板对这一项执行EvaluateNew:3构造函数调用
实际函数调用发生在调用AO处,调用AO时使用上下文确定的thisValue;这个参数在一长串与调用相关的调用中传递。Call调用函数的[[Call]]内部插槽。这将调用PrepareForOrdinaryCall,其中创建了一个新函数Environment Record:
函数环境记录是一个声明性环境记录,用于表示函数的顶级范围,如果函数不是ArrowFunction,则提供此绑定。如果函数不是ArrowFunction函数并引用了super,则其函数Environment Record还包含用于从函数内执行超级方法调用的状态。
此外,函数Environment Record中还有[[ThisValue]]字段:
这是用于此函数调用的This值。
NewFunctionEnvironment调用还设置函数环境的[[ThisBindingStatus]]属性。
[[Call]]还调用OrdinaryCallBindThis,其中适当的thisArgument是基于以下内容确定的:
原始参考,函数的类型,以及无论代码是否处于严格模式。
一旦确定,对新创建的函数Environment Record的BindThisValue方法的最后调用实际上会将[[ThisValue]]字段设置为thisArgument。
最后,这个字段是函数Environment Record的GetThisBinding AO从中获取值的位置:
函数Environment Record envRec〔…〕〔执行此操作〕的GetThisBinding具体方法:[…]3.返回envRec。[[ThisValue]]。
同样,如何精确地确定该值取决于许多因素;这只是一个概述。有了这个技术背景,让我们来看看所有具体的例子。
箭头功能
计算箭头函数时,函数对象的[[ThisMode]]内部槽在OrdinaryFunctionCreate中设置为“词法”。
在OrdinaryCallBindThis,它接受函数F:
设thisMode为F.[[thisMode]]。如果thisMode是词法的,则返回NormalCompletion(未定义)。[…]
这仅仅意味着绑定此的算法的其余部分被跳过。箭头函数不绑定自己的此值。
那么,在箭头函数中这是什么呢?回顾ResolveThisBinding和GetThisEnvironment,HasThisBinding方法显式返回false。
函数Environment Record envRec[…]的HasThisBinding具体方法[执行此操作]:如果envRec。[[ThisBindingStatus]]是词法的,返回false;否则,返回true。
因此,外部环境是迭代查找的。该过程将在具有此绑定的三个环境之一中结束。
这仅仅意味着,在箭头函数体中,这来自箭头函数的词法范围,或者换句话说(来自箭头函数与函数声明/表达式:它们是否等价/可交换?):
箭头函数没有自己的this[…]绑定。相反,[这个标识符]像任何其他变量一样在词法范围内解析。这意味着,在箭头函数内部,this[指的是]在定义箭头函数的环境中的[this值](即“在”箭头函数外部)。
函数财产
在普通函数(函数、方法)中,这取决于函数的调用方式。
这就是这些“语法变体”派上用场的地方。
考虑此对象包含函数:
const refObj = {
func: function(){
console.log(this);
}
};
或者:
const refObj = {
func(){
console.log(this);
}
};
在以下任何函数调用中,func中的this值将为refObj.1
refObj.func()refObj[“func”]()参考对象?。函数()参考对象函数?。()参考目标函数``
如果被调用的函数在语法上是基对象的属性,那么这个基将是调用的“引用”,在通常情况下,它将是这个的值。上述评估步骤解释了这一点;例如,在refObj.func()(或refObj[“func”]())中,CallMemberExpression是整个表达式refObj.func(),它由MemberExpression refObj.unc和Arguments()组成。
此外,refObj.func和refObj分别扮演三个角色:
它们都是表达式,他们都是推荐人,而且它们都是价值观。
refObj.func作为值是可调用函数对象;使用相应的引用来确定该绑定。
可选的链接和标记模板示例的工作方式非常相似:基本上,引用是?之前的所有内容?。()、“”之前或()之前。
EvaluateCall使用该引用的IsPropertyReference从语法上确定它是否是对象的属性。它正在尝试获取引用的[[Base]]属性(例如,当应用于refObj.func时为refObj;当应用于foo.bar.baz时为foo.bar)。如果将其作为属性写入,则GetThisValue将获取此[[Base]]属性并将其用作this值。
注意:关于这一点,Getters/Setter的工作方式与方法相同。简单的财产不会影响执行上下文,例如,这里是全局范围:
const o = {
a: 1,
b: this.a, // Is `globalThis.a`.
[this.a]: 2 // Refers to `globalThis.a`.
};
没有基引用、严格模式和
没有基引用的调用通常是不作为属性调用的函数。例如:
func(); // As opposed to `refObj.func();`.
传递或分配方法或使用逗号运算符时也会发生这种情况。这就是参考记录和值之间的差异。
注意函数j:按照规范,您将注意到j只能返回函数对象(Value)本身,而不能返回引用记录。因此,基础引用refObj丢失。
const g = (f) => f(); // No base ref.
const h = refObj.func;
const j = () => refObj.func;
g(refObj.func);
h(); // No base ref.
j()(); // No base ref.
(0, refObj.func)(); // Another common pattern to remove the base ref.
EvaluateCall调用此处未定义thisValue的Call。这在OrdinaryCallBindThis(F:函数对象;thisArgument:传递给Call的thisValue)中产生了不同:
设thisMode为F.[[thisMode]]。[…]如果thisMode是严格的,则将thisValue设为thisArgument。其他的如果thisArgument未定义或为null,则让globalEnv被称为Realm。[[GlobalEnv]]。[…]让thisValue为globalEnv。[[GlobalThisValue]]。其他的让这个价值成为现实!ToObject(thisArgument)。注意:ToObject生成包装对象[…]。[…]
注意:步骤5将this的实际值设置为严格模式下提供的thisArgument — 在这种情况下未定义。在“草率模式”中,未定义或空的thisArgument将导致此值为全局this值。
如果IsPropertyReference返回false,则EvaluateCall将执行以下步骤:
设refEnv为ref[[Base]]。断言:refEnv是一个环境记录。让thisValue为refEnv.WithBaseObject()。
这就是未定义的thisValue可能来自的地方:refEnv.WithBaseObject()始终未定义,with语句除外。在这种情况下,thisValue将是绑定对象。
还有Symbol.unscopables(MDN上的文档)来控制with绑定行为。
综上所述,迄今为止:
function f1(){
console.log(this);
}
function f2(){
console.log(this);
}
function f3(){
console.log(this);
}
const o = {
f1,
f2,
[Symbol.unscopables]: {
f2: true
}
};
f1(); // Logs `globalThis`.
with(o){
f1(); // Logs `o`.
f2(); // `f2` is unscopable, so this logs `globalThis`.
f3(); // `f3` is not on `o`, so this logs `globalThis`.
}
and:
"use strict";
function f(){
console.log(this);
}
f(); // Logs `undefined`.
// `with` statements are not allowed in strict-mode code.
请注意,在计算此值时,在哪里定义正常函数并不重要。
.call、.apply、.bind、thisArg和基元
OrdinaryCallBindThis的第5步与第6.2步(规范中的6.b)相结合的另一个结果是,一个基元只有在“草率”模式下才会将此值强制到一个对象。
为了检查这一点,让我们介绍this值的另一个源:重写this绑定的三个方法:4
函数.原型.应用(thisArg,argArray)Function.prototype.{call,bind}(thisArg,…args)
.bind创建一个绑定函数,其此绑定设置为thisArg,不能再次更改。call和.apply立即调用函数,并将此绑定设置为thisArg。
.call和.apply map直接应用于call,使用指定的thisArg。bind使用BoundFunctionCreate创建绑定函数。它们有自己的[[Call]]方法,该方法查找函数对象的[[BoundThis]]内部槽。
设置自定义此值的示例:
function f(){
console.log(this);
}
const myObj = {},
g = f.bind(myObj),
h = (m) => m();
// All of these log `myObj`.
g();
f.bind(myObj)();
f.call(myObj);
h(g);
对于对象,这在严格和非严格模式中是相同的。
现在,尝试提供原始值:
function f(){
console.log(this);
}
const myString = "s",
g = f.bind(myString);
g(); // Logs `String { "s" }`.
f.call(myString); // Logs `String { "s" }`.
在非严格模式下,原语被强制为其对象包装形式。这与调用object(“s”)或new String(“s)时得到的对象类型相同。在严格模式下,可以使用基本体:
"use strict";
function f(){
console.log(this);
}
const myString = "s",
g = f.bind(myString);
g(); // Logs `"s"`.
f.call(myString); // Logs `"s"`.
库使用这些方法,例如jQuery将this设置为此处选择的DOM元素:
$("button").click(function(){
console.log(this); // Logs the clicked button.
});
构造函数、类和新
当使用new运算符作为构造函数调用函数时,EvaluateNew调用Construct,后者调用[[Construct]]方法。如果函数是基构造函数(即不是类扩展…{…}),它将thisArgument设置为从构造函数的原型创建的新对象。在构造函数中对此设置的财产将结束于结果实例对象。这是隐式返回的,除非您显式返回自己的非基元值。
类是创建构造函数的一种新方法,在ECMAScript 2015中引入。
function Old(a){
this.p = a;
}
const o = new Old(1);
console.log(o); // Logs `Old { p: 1 }`.
class New{
constructor(a){
this.p = a;
}
}
const n = new New(1);
console.log(n); // Logs `New { p: 1 }`.
类定义隐式处于严格模式:
class A{
m1(){
return this;
}
m2(){
const m1 = this.m1;
console.log(m1());
}
}
new A().m2(); // Logs `undefined`.
超级的
使用new的行为的例外是类扩展…{…},如上所述。派生类在调用时不会立即设置其此值;它们只在通过一系列超级调用到达基类时才会这样做(在没有自己的构造函数的情况下隐式发生)。不允许在调用super之前使用此选项。
调用super将使用调用的词法范围(函数Environment Record)的此值调用超级构造函数。GetThisValue对超级调用有一个特殊的规则。它使用BindThisValue将其设置为该环境记录。
class DerivedNew extends New{
constructor(a, a2){
// Using `this` before `super` results in a ReferenceError.
super(a);
this.p2 = a2;
}
}
const n2 = new DerivedNew(1, 2);
console.log(n2); // Logs `DerivedNew { p: 1, p2: 2 }`.
5.评估类字段
ECMAScript 2022中引入了实例字段和静态字段。
对类求值时,执行ClassDefinitionEvaluation,修改正在运行的执行上下文。对于每个ClassElement:
如果一个字段是静态的,那么它指的是类本身,如果一个字段不是静态的,那么这将引用该实例。
私有字段(例如#x)和方法被添加到PrivateEnvironment中。
静态块目前为TC39第3阶段提案。静态块的工作方式与静态字段和方法相同:它们内部的这一点是指类本身。
注意,在方法和getters/setter中,这就像在普通函数财产中一样工作。
class Demo{
a = this;
b(){
return this;
}
static c = this;
static d(){
return this;
}
// Getters, setters, private modifiers are also possible.
}
const demo = new Demo;
console.log(demo.a, demo.b()); // Both log `demo`.
console.log(Demo.c, Demo.d()); // Both log `Demo`.
1:(o.f)()相当于o.f();(f) ()相当于f()。这在本文(存档)中进行了解释。请特别查看如何计算带圆括号的表达式。
2:它必须是MemberExpression,不能是属性,必须具有正好为“eval”的[[ReferencedName],并且必须是%eval%内在对象。
3:每当规范中说“让ref是对X求值的结果”,那么X就是需要找到求值步骤的表达式。例如,计算MemberExpression或CallExpression是这些算法之一的结果。其中一些会生成参考记录。
4:还有其他几种本机和主机方法允许提供this值,特别是Array.prototype.map、Array.prototype.forEach等,它们接受thisArg作为第二个参数。任何人都可以使用自己的方法来改变这一点,比如(func,thisArg)=>func.bind(thisArg),(func、thisAg)=>func.call(thisAg)等。一如既往,MDN提供了很好的文档。
只是为了好玩,用一些例子来测试你的理解
对于每个代码段,回答以下问题:“标记行的值是多少?为什么?”。
要显示答案,请单击灰色框。
if(真){console.log(this);//这是什么?}globalThis。标记行在初始全局执行上下文中求值。常量obj={};函数myFun(){return{//这里的“this”是什么?“is obj”:this==obj,“is globalThis”:this==globalThis};}obj.method=myFun;console.log(obj.method());当将函数作为对象的属性调用时,将此绑定设置为引用obj.method的基,即obj。常量对象={myMethod:函数(){return{//这里的“this”是什么?“is obj”:this==obj,“is globalThis”:this==globalThis};}},myFun=obj.myMethod;console.log(myFun());globalThis。由于函数值myFun/obj.myMMethod未从对象中调用,因此作为属性,此绑定将为globalThis。这与Python不同,在Python中,访问方法(obj.myMethod)会创建绑定的方法对象。常量对象={myFun:()=>({//这里的“this”是什么?“is obj”:this==obj,“is globalThis”:this==globalThis})};console.log(obj.myFun());globalThis。箭头函数不会创建自己的此绑定。词法作用域与初始全局作用域相同,因此这是globalThis。函数myFun(){console.log(this);//这是什么?}常量对象={myMethod:函数(){eval(“myFun()”);}};obj.myMethod();globalThis。当计算直接eval调用时,这是obj。然而,在eval代码中,myFun没有从对象中调用,因此this绑定被设置为全局对象。函数myFun(){//这是什么?返回{“is obj”:this==obj,“is globalThis”:this==globalThis};}常量obj={};console.log(myFun.call(obj));obj.行myFun.call(obj);正在调用特殊的内置函数function.prototype.call,该函数接受thisArg作为第一个参数。类MyCls{arrow=()=>({//这里的“this”是什么?“是MyCls”:这==MyCls,“is globalThis”:this==globalThis,“is instance”:MyCls的此实例});}console.log(新MyCls().arrow());这是MyCls的实例。箭头函数不会更改此绑定,因此它来自词法范围。因此,这与上面提到的类字段完全相同,如a=this;。尝试将其更改为静态箭头。你得到你期望的结果了吗?
关于此关键字的一些信息
让我们将此关键字记录到全局范围内的控制台中,无需任何代码
console.log(this)
在客户端/浏览器中,此关键字是一个全局对象,它是窗口
console.log(this === window) // true
and
在Server/Node/Javascript运行时中,此关键字也是一个全局对象,即module.exports
console.log(this === module.exports) // true
console.log(this === exports) // true
请记住,导出只是对module.exports的引用