我希望找到一个“this”关键字的明确解释,以及如何正确使用它。

它的行为似乎很奇怪,我不完全明白为什么。

这是如何工作的,何时使用?


当前回答

这里有一个很好的JavaScript源代码。

以下是总结:

全局this在浏览器中,在全局范围内,这是windowobject<script type=“text/javascript”>console.log(此==窗口);//真的var foo=“bar”;console.log(this.foo);//“条形图”console.log(window.foo);//“条形图”在使用repl的节点中,这是顶级命名空间。您可以将其称为全局。>这个{ArrayBuffer:[Function:ArrayBuffer],Int8Array:{[Function:Int8Array]BYTES_PER_ELEMENT:1},Uint8Array:{[Function:Uint8Array]BYTES_PER_ELEMENT:1},...>全局==此真的在从脚本执行的节点中,全局范围中的这个对象以空对象开始。它与全球不同\\测试.jsconsole.log(此);\\{}console.log(this==全局);\\时尚函数this

除了在DOM事件处理程序的情况下,或者当提供了thisArg时(请参见下文),在节点和浏览器中都会在不使用新引用调用全局范围的函数中使用此函数…

<script type="text/javascript">
    foo = "bar";

    function testThis() {
      this.foo = "foo";
    }

    console.log(this.foo); //logs "bar"
    testThis();
    console.log(this.foo); //logs "foo"
</script>

如果使用use strict;,在这种情况下,这将是未定义的

<script type="text/javascript">
    foo = "bar";

    function testThis() {
      "use strict";
      this.foo = "foo";
    }

    console.log(this.foo); //logs "bar"
    testThis();  //Uncaught TypeError: Cannot set property 'foo' of undefined 
</script>

若使用new调用函数,这将是一个新上下文,它将不会引用全局this。

<script type="text/javascript">
    foo = "bar";

    function testThis() {
      this.foo = "foo";
    }

    console.log(this.foo); //logs "bar"
    new testThis();
    console.log(this.foo); //logs "bar"

    console.log(new testThis().foo); //logs "foo"
</script>

原型

您创建的函数将成为函数对象。它们会自动获得一个特殊的原型属性,这是您可以为其赋值的属性。当您使用new调用函数来创建实例时,您可以访问分配给原型属性的值。您可以使用此访问这些值。

function Thing() {
  console.log(this.foo);
}

Thing.prototype.foo = "bar";

var thing = new Thing(); //logs "bar"
console.log(thing.foo);  //logs "bar"

在原型上分配数组或对象通常是错误的。如果希望每个实例都有自己的数组,请在函数中创建它们,而不是在原型中创建它们。

function Thing() {
    this.things = [];
}

var thing1 = new Thing();
var thing2 = new Thing();
thing1.things.push("foo");
console.log(thing1.things); //logs ["foo"]
console.log(thing2.things); //logs []

反对这个

您可以在对象的任何函数中使用它来引用该对象的其他财产。这与使用new创建的实例不同。

var obj = {
    foo: "bar",
    logFoo: function () {
        console.log(this.foo);
    }
};

obj.logFoo(); //logs "bar"

DOM事件this

在HTML DOM事件处理程序中,这始终是对事件附加到的DOM元素的引用

function Listener() {
    document.getElementById("foo").addEventListener("click",
       this.handleClick);
}
Listener.prototype.handleClick = function (event) {
    console.log(this); //logs "<div id="foo"></div>"
}

var listener = new Listener();
document.getElementById("foo").click();

除非您绑定上下文

function Listener() {
    document.getElementById("foo").addEventListener("click", 
        this.handleClick.bind(this));
}
Listener.prototype.handleClick = function (event) {
    console.log(this); //logs Listener {handleClick: function}
}

var listener = new Listener();
document.getElementById("foo").click();

HTML此

在可以放入JavaScript的HTML属性中,这是对元素的引用。

<div id="foo" onclick="console.log(this);"></div>
<script type="text/javascript">
document.getElementById("foo").click(); //logs <div id="foo"...
</script>

评估此

您可以使用eval访问此。

function Thing () {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
    eval("console.log(this.foo)"); //logs "bar"
}

var thing = new Thing();
thing.logFoo();

用这个

您可以使用with将其添加到当前范围中,以读取和写入其值,而无需显式引用它。

function Thing () {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
    with (this) {
        console.log(foo);
        foo = "foo";
    }
}

var thing = new Thing();
thing.logFoo(); // logs "bar"
console.log(thing.foo); // logs "foo"

jQuery this

jQuery在很多地方都会引用DOM元素。

<div class="foo bar1"></div>
<div class="foo bar2"></div>
<script type="text/javascript">
$(".foo").each(function () {
    console.log(this); //logs <div class="foo...
});
$(".foo").on("click", function () {
    console.log(this); //logs <div class="foo...
});
$(".foo").each(function () {
    this.click();
});
</script>

其他回答

这是JavaScript中一个被误解的概念,因为它在不同地方的行为几乎不同。简单地说,这是指我们当前正在执行的函数的“所有者”。

这有助于获取我们使用的当前对象(也称为执行上下文)。如果您了解当前函数在哪个对象中执行,那么您可以很容易地了解这是什么当前函数

var val = "window.val"

var obj = {
    val: "obj.val",
    innerMethod: function () {
        var val = "obj.val.inner",
            func = function () {
                var self = this;
                return self.val;
            };

        return func;
    },
    outerMethod: function(){
        return this.val;
    }
};

//This actually gets executed inside window object 
console.log(obj.innerMethod()()); //returns window.val

//Breakdown in to 2 lines explains this in detail
var _inn = obj.innerMethod();
console.log(_inn()); //returns window.val

console.log(obj.outerMethod()); //returns obj.val

上面我们创建了3个同名“val”的变量。一个在全局上下文中,一个在obj内部,另一个在obj的innerMethod内部。JavaScript通过从本地到全局的作用域链来解析特定上下文中的标识符。


很少有地方可以区分这一点

调用对象的方法

var status = 1;
var helper = {
    status : 2,
    getStatus: function () {
        return this.status;
    }
};

var theStatus1 = helper.getStatus(); //line1
console.log(theStatus1); //2

var theStatus2 = helper.getStatus;
console.log(theStatus2()); //1

当执行第1行时,JavaScript为函数调用建立一个执行上下文(EC),将其设置为最后一个“.”之前引用的对象。因此在最后一行中,您可以理解a()是在全局上下文(即窗口)中执行的。

使用构造函数

这可用于引用正在创建的对象

function Person(name){
    this.personName = name;
    this.sayHello = function(){
        return "Hello " + this.personName;
    }
}

var person1 = new Person('Scott');
console.log(person1.sayHello()); //Hello Scott

var person2 = new Person('Hugh');
var sayHelloP2 = person2.sayHello;
console.log(sayHelloP2()); //Hello undefined

当执行new Person()时,将创建一个全新的对象。调用Person,并将其this设置为引用该新对象。

函数调用

function testFunc() {
    this.name = "Name";
    this.myCustomAttribute = "Custom Attribute";
    return this;
}

var whatIsThis = testFunc();
console.log(whatIsThis); //window

var whatIsThis2 = new testFunc();
console.log(whatIsThis2);  //testFunc() / object

console.log(window.myCustomAttribute); //Custom Attribute 

如果我们错过了新的关键字,whatIsThis指的是它能找到的最全局的上下文(window)

使用事件处理程序

如果事件处理程序是内联的,则它引用全局对象

<script type="application/javascript">
    function click_handler() {
        alert(this); // alerts the window object
    }
</script>

<button id='thebutton' onclick='click_handler()'>Click me!</button>

通过JavaScript添加事件处理程序时,这是指生成事件的DOM元素。


您还可以使用.apply().call()和.bind()操作上下文JQuery代理是另一种方法,您可以使用它来确保函数中的值符合您的要求。(检查了解$.proxy()、jQuery.proxy)用法)var that=this在JavaScript中意味着什么

javascript中的每个执行上下文都有一个this参数,该参数由以下参数设置:

如何调用函数(包括作为对象方法、调用和应用的使用、new的使用)绑定的使用箭头函数的词汇(它们采用外部执行上下文的this)代码是严格模式还是非严格模式是否使用eval调用代码

可以使用func.all、func.apply或func.bind设置此值。

默认情况下,当在DOM元素上引发事件后调用监听器时,函数的这个值就是DOM元素,这让大多数初学者感到困惑。

jQuery使用jQuery.proxy进行更改变得很简单。

JavaScript中的“this”这是执行上下文的财产之一。

每次执行函数时都会创建此属性,而不是在此之前。它的价值不是静态的,而是取决于它的使用方式。获取一个指向其所在函数所有者的值习惯于

“this”关键字有不同的使用方式,下面是它的示例(方法、正则函数、箭头函数、事件监听器、显式函数绑定)。

1.方法内部。

this==(对调用方法的对象)。

在上面的示例中,方法“fullName()”由对象“person”调用,因此方法“fullName()”中的值将等于对象“person”。

2.函数内部。

i) 函数声明/表达式

在松散模式下,此==窗口(对象)

在严格模式下,this==未定义

注意:使用函数声明或函数表达式方法定义函数时,此属性的工作原理相同。

ii)箭头功能:

箭头函数没有自己的此属性,它们将此属性的值作为其周围的函数。如果周围函数不存在,即如果它们是在全局级别定义的,则此==窗口(对象)

3.事件侦听器this==附加处理程序的对象。单击事件绑定到Document对象

在上面的示例中,由于单击处理程序附加到“document”对象,因此它将等于“document“对象

4.显式函数绑定(调用、应用、绑定)

call()和apply()方法是预定义的JavaScript方法。

它们都可以用来调用另一个对象作为参数的对象方法。

在上面的示例中,“printFullDetails()”中的这一点通过传递作为调用方法的第一个参数显式设置为personObj1和personObj2。

您可以在这里探索有关调用、应用和绑定方法的更多信息。

关于此关键字的一些信息

让我们将此关键字记录到全局范围内的控制台中,无需任何代码

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的引用

这是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;。尝试将其更改为静态箭头。你得到你期望的结果了吗?