“this”关键字是如何工作的,什么时候应该使用它?

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

它似乎表现得很奇怪,我不完全理解为什么。

this是如何工作的,什么时候应该使用它?

451784 次浏览

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

  1. 如何调用函数(包括作为对象方法,使用打电话适用,使用新的
  2. 使用绑定
  3. 箭头函数的词法(它们采用外部执行上下文的这个
  4. 代码是否处于严格或非严格模式
  5. 代码是否使用#0调用

您可以使用func.callfunc.applyfunc.bind设置这个的值。

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

jQuery使这个微不足道的改变与jQuery.proxy.

#0是JavaScript中的关键字,是执行上下文的属性。它的主要用途是函数和构造函数。this的规则非常简单(如果你坚持最佳实践)。

规范中this的技术说明

ECMAScript标准通过抽象操作(缩写为AO解决这个绑定定义了#0

[AO]ResolveThisB的绑定[…]使用运行执行上下文的L的环境来确定关键字this的绑定。[步骤]:

  1. envRec获取此环境信息()。
  2. 返回?envRec。GetThisB的()。

全球环境记录模块环境记录功能环境记录都有自己的GetThisB的方法。

获取此环境信息 AO查找当前运行执行上下文的L的环境,并找到最接近的具有这个绑定的优势环境记录(通过迭代访问它们的[[OuterEnv]]属性)(即HasThisB的返回真正)。此过程以三种环境记录类型之一结束。

this的值通常取决于代码是否在严格模式中。

GetThisBind的返回值反映了当前执行上下文this的值,因此每当建立新的执行上下文时,this都会解析为一个不同的值。当修改当前执行上下文时,也会发生这种情况。以下小节列出了可能发生这种情况的五种情况。

您可以将代码示例放在AST浏览器中,以跟随规范详细信息。

1.脚本中的全局执行上下文

这是在顶层评估的脚本代码,例如直接在<script>中:

<script>// Global contextconsole.log(this); // Logs global object.
setTimeout(function(){console.log("Not global context");});</script>

在脚本的初始全局执行上下文中,评估this会导致正在绑定中执行以下步骤:

全局环境记录envRec[…]的GetThisB的具体方法[执行此操作]:

  1. 返回envRec.[[GlobalThisValue]]。

全局环境记录的[[GlobalThisValue]]属性始终设置为主机定义的全局对象,可通过#0(Web上的window,Node.js上的globalMDN上的文档)访问。按照初始化主机定义域的步骤了解[[GlobalThisValue]]属性是如何产生的。

2.模块中的全局执行上下文

模块已在ECMAScript 2015中引入。

这适用于模块,例如直接在<script type="module">中,而不是简单的<script>

当在模块的初始全局执行上下文中时,评估this会导致正在绑定中采取以下步骤:

模块环境记录[…]的GetThisB的具体方法[执行此操作]:

  1. 返回未定义

在模块中,this的值在全局上下文中总是undefined。模块隐含在严格模式中。

3.输入val代码

有两种eval调用:直接间接。这种区别自ECMAScript第5版以来就存在了。

  • 直接eval调用通常看起来像eval();(eval)();(或((eval))();等)。1如果调用表达式适合窄模式,则只有直接2
  • 间接eval调用涉及以任何其他方式调用函数引用eval。它可以是eval?.()(, eval)()window.eval()eval.call(eval0…)等。给定eval2,它也将是eval3…)eval5…)。另外,给定eval7,调用eval8…)也将是间接的。

请参阅Chuckj对“JavaScript中的(1, ava)('this')vs ava('this')?”的回答详细介绍Dmitry Soshnikov的ECMA-262-5-第2章:严格模式存档)了解何时可以使用间接eval()调用。

是否执行执行eval代码。它创建了一个新的声明式环境记录作为其词典环境,这是获取此环境信息获取this值的地方。

然后,如果this出现在eval代码中,则调用获取此环境信息找到的环境记录的GetThisB的方法并返回其值。

创建的声明式环境记录取决于eval调用是直接的还是间接的:

这意味着:

  • 在直接评估中,this值不会改变;它取自调用eval的词法范围。
  • 在间接评估中,this值是全局对象(globalThis)。

#0怎么样?-#0类似于eval,但它不会立即调用代码;它创建了一个函数。这个绑定在这里的任何地方都不适用,除非调用函数时,它正常工作,如下一小节所述。

4.输入函数代码

输入函数代码发生在呼吁函数时。

调用函数有四类语法。

实际的函数调用发生在打电话 AO处,调用时根据上下文确定这个值;此参数在一长串与调用相关的调用中传递。打电话调用函数的[[呼叫]]内部槽。这调用准备普通呼叫,其中创建了一个新的函数环境记录

函数环境记录是一个声明性环境记录,用于表示函数的顶级范围,如果函数不是箭头函数,则提供this绑定。如果函数不是箭头函数函数并引用super,其函数环境记录还包含用于从函数内执行super方法调用的状态。

此外,函数环境记录中还有[[ThisValue]]字段:

这是用于调用函数的this值。

新功能环境调用还设置函数环境的[[ThisBindingStatus]]属性。

[[呼叫]]也调用普通调用绑定,其中适当的这个论点是基于以下因素确定的:

  • 原始参考,
  • 功能的类型,以及
  • 代码是否在严格模式中。

一旦确定,对新创建的函数环境记录的绑定这个值方法的最终调用实际上将[[ThisValue]]字段设置为这个论点

最后,这个字段是函数环境记录正在绑定中 AO从this获取值的地方:

函数环境记录envRec[…][执行此操作]:

[…]
3.返回envRec.[[ThisValue]]。

同样,如何准确地确定这个值取决于许多因素;这只是一个一般的概述。有了这个技术背景,让我们检查所有具体的例子。

箭头函数

当计算箭头函数时,函数对象的[[ThisMode]]内部槽在普通函数创建中设置为“词汇”

普通调用绑定,它接受一个函数F

  1. thisModeF。[[ThisMode]]。
  2. 如果thisMode词汇,则返回NormalCompl的(undefined)。[…]

这只是意味着绑定这个的其余算法被跳过。箭头函数不绑定自己的这个值。

那么,箭头函数中的this是什么呢?回顾解决这个绑定获取此环境信息方法显式返回false

函数环境记录envRec[…][执行此操作]的具体方法:

  1. 如果envRec.[[ThisBindingStatus]]是词汇,返回虚假;否则返回真正

因此,外部环境被迭代地查找。该过程将在具有这个绑定的三个环境之一中结束。

这只是意味着,在箭头函数体中,#0来自箭头函数的词法范围,或者换句话说(来自箭头函数与函数声明/表达式:它们是等价的/可交换的吗?):

箭头函数没有自己的this[…]绑定。相反,[此标识符]像任何其他变量一样在词法范围内解析。这意味着在箭头函数内部,this[引用]环境中的[this的值]箭头函数是定义(即箭头函数的“外部”)。

功能属性

在正常函数(function方法)中,this被确定为如何调用函数

这就是这些“语法变体”派上用场的地方。

考虑这个包含函数的对象:

const refObj = {func: function(){console.log(this);}};

或者:

const refObj = {func(){console.log(this);}};

在以下任何函数调用中,func中的this值将是refObj1

  • refObj.func()
  • refObj["func"]()

如果被调用的函数在语法上是一个base对象的属性,那么这个base将是调用的“引用”,在通常情况下,它将是this的值。这可以通过上面链接的评估步骤来解释;例如,在refObj.func()(或refObj["func"]())中,是整个表达式refObj.func(),它由成员表达式refObj.func论点()组成。

但是,refObj.funcrefObj扮演三个角色,每个角色:

  • 它们都是表达,
  • 他们都是推荐人,而且
  • 两者都是价值观。

refObj.func作为是可调用的函数对象;相应的参考用于确定this绑定。

可选的链接和标记模板示例的工作方式非常相似:基本上,引用是?.()之前、``之前或()之前的所有内容。

评估结果使用该引用的是否必填属性引用来确定它是否是对象的属性。它试图获取引用的[[Base]]属性(例如,当应用于refObj.func时为refObj;或应用于foo.bar.baz时为foo.bar)。如果它被写入属性,那么获取此值将获取此[[Base]]属性并将其用作这个值。

注意:关于thisGetters/Setters的工作方式与方法相同。简单的属性不会影响执行上下文,例如,在这里,this在全局范围内:

const o = {a: 1,b: this.a, // Is `globalThis.a`.[this.a]: 2 // Refers to `globalThis.a`.};

不带基引用、严格模式和#0的调用

没有基引用的调用通常是不作为属性调用的函数。例如:

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.

评估结果在这里用未定义中的这个值调用打电话。这与普通调用绑定有所不同(F:函数对象;这个论点:传递给打电话这个值):

  1. thisModeF。[[ThisMode]]。

[…]

  1. 如果thisMode严格,让这个值这个论点
  2. 否则,
    1. 如果这个论点未定义null,则
      1. global alEnv参数名。[[GlobalEnv]]。
      2. […]
      3. 这个值global alEnv.[[GlobalThisValue]]。
    2. 否则,
      1. 这个值成为!创建对象(这个论点)。
      2. 注意:创建对象产生包装对象[…]。

[…]

注意:步骤5在严格模式下将this的实际值设置为提供的这个论点-在这种情况下为undefined。在“草率模式”中,未定义或null这个论点导致this是全局这个值。

如果是否必填属性引用返回虚假,则评估结果执行以下步骤:

  1. refEnv参考.[[Base]]。
  2. 断言:refEnv是一个环境记录。
  3. 这个值refEnv。带有BaseObject()。

这就是未定义的这个值可能来自的地方:refEnv使用基础对象()在#0语句中总是未定义除了。在这种情况下,这个值将是绑定对象。

还有#0MDN上的文档)来控制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`.}

和:

"use strict";
function f(){console.log(this);}
f(); // Logs `undefined`.
// `with` statements are not allowed in strict-mode code.

请注意,当评估this在哪里定义正常函数并不重要时。

  • Function.prototype.apply(thisArg, argArray)
  • Function.prototype.{callbind}(thisArg, ...args)

#0创建一个绑定函数,其这个绑定设置为这个参数,不能再次更改。#1#2立即调用该函数,这个绑定设置为这个参数

.call.apply直接映射到打电话,使用指定的这个参数.bind使用边界函数创建入口创建一个绑定函数。这些有自己调用方法查找函数对象的[[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.});

构造函数#0

当使用new运算符作为构造函数调用函数时,评估状态调用构建,后者调用构造方法。如果该函数是基本构造函数(即不是class extends{}),它将这个论点设置为从构造函数原型创建的新对象。在构造函数中this上设置的属性将最终出现在结果实例对象上。this被隐式返回,除非您显式返回自己的非原始值。

#0是一种创建构造函数的新方法,在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`.

super

new的例外是class extends{},如上所述。派生类在调用时不会立即设置它们的这个值;它们只有在通过一系列super调用到达基类时才会这样做(在没有自己的constructor的情况下隐式发生)。不允许在调用super之前使用this

调用#0使用调用的词法范围(函数环境记录)的这个值调用超级构造函数。获取此值super调用有一个特殊规则。它使用绑定这个值this设置为该环境记录。

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中引入。

当评估class时,执行类别定义评估,修改运行执行上下文。对于每个类元素

  • 如果一个字段是静态的,那么this指的是类本身,
  • 如果字段不是静态的,则this引用实例。

私有字段(例如#x)和方法被添加到私有环境中。

静态块目前是TC39第三阶段提案。静态块的工作方式与静态字段和方法相同:其中的this指的是类本身。

请注意,在方法和getters/setters中,this的工作方式与普通函数属性相同。

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()。这在这篇2ality文章存档)中有解释。特别参见如何计算括号表达式

2:它必须是成员表达式,不能是属性,必须具有恰好为"ava"的[[引用名称]],并且必须是%ava%内部对象。

3:每当规范说“让 ref是评估的结果 X."时,那么X就是您需要找到评估步骤的某个表达式。例如,评估成员表达式呼叫表达这些算法之一的结果。其中一些结果是参考记录

4:还有其他几种本机和主机方法允许提供这个值,特别是Array.prototype.mapArray.prototype.forEach等,它们接受这个参数作为第二个参数。任何人都可以创建自己的方法来更改this,如(func, thisArg) => func.bind(thisArg)(func, thisArg) => func.call(thisArg)等。一如既往,MDN提供了很好的留档。


只是为了好玩,用一些例子来测试你的理解

对于每个代码片段,回答问题:"#0在标记行的值是多少?为什么?"

要显示答案,请单击灰色框。

  1. if(true){console.log(this); // What is `this` here?}

    globalThis。标记的行在初始全局执行上下文中进行评估。

  2. const obj = {};
    function myFun(){return { // What is `this` here?"is obj": this === obj,"is globalThis": this === globalThis};}
    obj.method = myFun;
    console.log(obj.method());
    

    obj。当作为对象的属性调用函数时,调用时将这个绑定设置为引用obj.method基地,即obj

  3. const obj = {myMethod: function(){return { // What is `this` here?"is obj": this === obj,"is globalThis": this === globalThis};}},myFun = obj.myMethod;
    console.log(myFun());
    

    globalThis。由于函数值myFun/obj.myMethod没有从对象中调用,作为属性,这个绑定将为globalThis。 这与Python不同,在Python中访问方法(obj.myMethod)会创建绑定方法对象

  4. const obj = {myFun: () => ({ // What is `this` here?"is obj": this === obj,"is globalThis": this === globalThis})};
    console.log(obj.myFun());
    

    globalThis。箭头函数不会创建自己的这个绑定。词法范围与初始全局范围相同,因此thisglobalThis

  5. function myFun(){console.log(this); // What is `this` here?}
    const obj = {myMethod: function(){eval("myFun()");}};
    obj.myMethod();

    globalThis。当评估直接的val调用时,thisobj。然而,在val代码中,myFun没有从对象中调用,因此这个绑定设置为全局对象。

  6. function myFun() {// What is `this` here?return {"is obj": this === obj,"is globalThis": this === globalThis};}
    const obj = {};
    console.log(myFun.call(obj));
    

    obj。第1行调用特殊的内置函数Function.prototype.call,它接受thisArg作为第一个参数。

  7. class MyCls{arrow = () => ({ // What is `this` here?"is MyCls": this === MyCls,"is globalThis": this === globalThis,"is instance": this instanceof MyCls});}
    console.log(new MyCls().arrow());
    

    它是MyCls的实例。箭头函数不会更改这个绑定,因此它来自词法范围。因此,这是完全一样,就像上面提到的类字段一样,就像a = this;。尝试将其更改为static arrow。你得到你期望的结果了吗?

这个有什么帮助?(javascript中'this'最令人困惑的是,它通常不链接到您的对象,而是链接到当前的执行范围-这可能不完全是它的工作方式,但对我来说总是这样-请参阅文章以获得完整的解释)

“this”是关于作用域的。每个函数都有自己的作用域,因为JS中的一切都是对象,即使是函数也可以使用“this”将一些值存储到自己体内。OOP 101教导“this”只适用于对象的实例。因此,每次执行函数时,该函数的新“实例”都有“this”的新含义。

大多数人在尝试在匿名闭包函数中使用“this”时会感到困惑,例如:

(function(value) {this.value = value;$('.some-elements').each(function(elt){elt.innerHTML = this.value;        // uh oh!! possibly undefined});})(2);

所以在这里,在each()内部,“this”并没有包含你期望它的“值”(从它上面的

this.value=价值;
开始)。所以,为了克服这个(没有双关语)问题,开发人员可以:

(function(value) {var self = this;            // small changeself.value = value;$('.some-elements').each(function(elt){elt.innerHTML = self.value;        // phew!! == 2});})(2);

尝试一下;你会开始喜欢这种编程模式

丹尼尔,很棒的解释!关于这一点的几句话以及事件处理程序情况下的this执行上下文指针的好列表。

简而言之,JavaScript中的this指向当前函数运行的对象(或从其执行上下文),并且它始终是只读的,无论如何都无法设置它(这样的尝试最终会导致“无效的左侧分配”消息。

对于事件处理程序:内联事件处理程序,例如<element onclick="foo">,会覆盖之前和之前附加的任何其他处理程序,因此要小心,最好完全远离内联事件委托。感谢Zara Alaverdyan,他通过不同的辩论激励我列出了这个例子:)

  • el.onclick = foo; // in the foo - obj
  • el.onclick = function () {this.style.color = '#fff';} // obj
  • el.onclick=Function(){做某事();} // 在做某事中-窗口
  • el.addEventListener('click',foo,false) // in the foo - obj
  • el.attachEvent('onClick,函数 () { // }') // 窗口符合IE:)
  • <button onclick="this.style.color = '#fff';"> // obj
  • <按钮onClick="foo">//在foo窗口中,但您可以<按钮

this关键字在JavaScript中的表现与其他语言不同。在面向对象语言中,this关键字指的是类的当前实例。在JavaScript中,this的值由函数(#3)的调用上下文和调用位置决定。

1.在全球范围内使用时

当您在全局上下文中使用this时,它绑定到全局对象(浏览器中的window

document.write(this);  //[object Window]

当您在全局上下文中定义的函数中使用this时,this仍然绑定到全局对象,因为该函数实际上是全局上下文的方法。

function f1(){return this;}document.write(f1());  //[object Window]

f1以上是全局对象的方法。因此我们也可以在window对象上调用它,如下所示:

function f(){return this;}
document.write(window.f()); //[object Window]

2.当使用内部对象方法时

当您在对象方法中使用this关键字时,this绑定到“立即”封闭对象。

var obj = {name: "obj",f: function () {return this + ":" + this.name;}};document.write(obj.f());  //[object Object]:obj

在上面,我用双引号引用了立即这个词。这是为了表明,如果您将对象嵌套在另一个对象中,那么this将绑定到直接父对象。

var obj = {name: "obj1",nestedobj: {name:"nestedobj",f: function () {return this + ":" + this.name;}}}
document.write(obj.nestedobj.f()); //[object Object]:nestedobj

即使您将函数作为方法显式添加到对象中,它仍然遵循上述规则,即this仍然指向直接父对象。

var obj1 = {name: "obj1",}
function returnName() {return this + ":" + this.name;}
obj1.f = returnName; //add method to objectdocument.write(obj1.f()); //[object Object]:obj1

3.调用无上下文函数时

当您在没有任何上下文(即不在任何对象上)调用的函数中使用this时,它将绑定到全局对象(浏览器中的window)(即使函数是在对象内部定义的)。

var context = "global";
var obj = {context: "object",method: function () {function f() {var context = "function";return this + ":" +this.context;};return f(); //invoked without context}};
document.write(obj.method()); //[object Window]:global

尝试所有功能

我们也可以用函数尝试上述几点。但是有一些差异。

  • 上面我们使用对象文字表示法将成员添加到对象中。我们可以通过使用this将成员添加到函数中。来指定它们。
  • 对象文字表示法创建了一个我们可以立即使用的对象实例。对于函数,我们可能需要首先使用new运算符创建其实例。
  • 同样在对象文字方法中,我们可以使用点操作符显式地将成员添加到已经定义的对象中。这只添加到特定的实例中。但是,我已经向函数原型添加了变量,以便它在函数的所有实例中得到反映。

下面我尝试了我们使用Object和上面的this所做的所有事情,但首先创建函数而不是直接编写对象。

/*********************************************************************1. When you add variable to the function using this keyword, itgets added to the function prototype, thus allowing all functioninstances to have their own copy of the variables added.*********************************************************************/function functionDef(){this.name = "ObjDefinition";this.getName = function(){return this+":"+this.name;}}
obj1 = new functionDef();document.write(obj1.getName() + "<br />"); //[object Object]:ObjDefinition
/*********************************************************************2. Members explicitly added to the function protorype also behaveas above: all function instances have their own copy of thevariable added.*********************************************************************/functionDef.prototype.version = 1;functionDef.prototype.getVersion = function(){return "v"+this.version; //see how this.version refers to the//version variable added through//prototype}document.write(obj1.getVersion() + "<br />"); //v1
/*********************************************************************3. Illustrating that the function variables added by both aboveways have their own copies across function instances*********************************************************************/functionDef.prototype.incrementVersion = function(){this.version = this.version + 1;}var obj2 = new functionDef();document.write(obj2.getVersion() + "<br />"); //v1
obj2.incrementVersion();      //incrementing version in obj2//does not affect obj1 version
document.write(obj2.getVersion() + "<br />"); //v2document.write(obj1.getVersion() + "<br />"); //v1
/*********************************************************************4. `this` keyword refers to the immediate parent object. If younest the object through function prototype, then `this` insideobject refers to the nested object not the function instance*********************************************************************/functionDef.prototype.nestedObj = { name: 'nestedObj',getName1 : function(){return this+":"+this.name;}};
document.write(obj2.nestedObj.getName1() + "<br />"); //[object Object]:nestedObj
/*********************************************************************5. If the method is on an object's prototype chain, `this` refersto the object the method was called on, as if the method was onthe object.*********************************************************************/var ProtoObj = { fun: function () { return this.a } };var obj3 = Object.create(ProtoObj); //creating an object setting ProtoObj//as its prototypeobj3.a = 999;                       //adding instance member to obj3document.write(obj3.fun()+"<br />");//999//calling obj3.fun() makes//ProtoObj.fun() to access obj3.a as//if fun() is defined on obj3

4.在构造函数内部使用时

当函数用作构造函数时(即用new关键字调用它时),函数体中的this指向正在构造的新对象。

var myname = "global context";function SimpleFun(){this.myname = "simple function";}
var obj1 = new SimpleFun(); //adds myname to obj1//1. `new` causes `this` inside the SimpleFun() to point to the//   object being constructed thus adding any member//   created inside SimipleFun() using this.membername to the//   object being constructed//2. And by default `new` makes function to return newly//   constructed object if no explicit return value is specified
document.write(obj1.myname); //simple function

5.当在原型链上定义的函数内部使用时

如果该方法位于对象的原型链上,则该方法中的this引用调用该方法的对象,就好像该方法是在对象上定义的一样。

var ProtoObj = {fun: function () {return this.a;}};//Object.create() creates object with ProtoObj as its//prototype and assigns it to obj3, thus making fun()//to be the method on its prototype chain
var obj3 = Object.create(ProtoObj);obj3.a = 999;document.write(obj3.fun()); //999
//Notice that fun() is defined on obj3's prototype but//`this.a` inside fun() retrieves obj3.a

6.内部调用()、应用()和绑定()函数

  • 所有这些方法都在Function.prototype上定义。
  • 这些方法允许编写一次函数并在不同的上下文中调用它。换句话说,它们允许指定this的值,该值将在函数执行时使用。它们还接受调用时传递给原始函数的任何参数。
  • #0fun()中将obj1设置为this的值,并调用fun()传递argsArray的元素作为其参数。
  • #0-在fun()内将obj1设置为this的值,并调用fun()传递arg1, arg2, arg3, ...作为其参数。
  • #0-返回对函数fun的引用,其中this在乐趣中绑定到obj1fun的参数绑定到指定的参数arg1, arg2, arg3,...
  • 到目前为止,applycallbind之间的区别一定很明显了。apply允许指定参数作为类似数组的对象,即具有数字length属性和相应的非负整数属性的对象。而call允许直接指定函数的参数。applycall都立即在指定的上下文和指定的参数中调用函数。另一方面,bind简单地返回绑定到指定的this值和参数的函数。我们可以通过将其分配给变量来捕获对该返回函数的引用,然后我们可以随时调用它。
function add(inc1, inc2){return this.a + inc1 + inc2;}
var o = { a : 4 };document.write(add.call(o, 5, 6)+"<br />"); //15//above add.call(o,5,6) sets `this` inside//add() to `o` and calls add() resulting:// this.a + inc1 + inc2 =// `o.a` i.e. 4 + 5 + 6 = 15document.write(add.apply(o, [5, 6]) + "<br />"); //15// `o.a` i.e. 4 + 5 + 6 = 15
var g = add.bind(o, 5, 6);       //g: `o.a` i.e. 4 + 5 + 6document.write(g()+"<br />");    //15
var h = add.bind(o, 5);          //h: `o.a` i.e. 4 + 5 + ?document.write(h(6) + "<br />"); //15// 4 + 5 + 6 = 15document.write(h() + "<br />");  //NaN//no parameter is passed to h()//thus inc2 inside add() is `undefined`//4 + 5 + undefined = NaN</code>

7.this内部事件处理程序

  • 当您将函数直接分配给元素的事件处理程序时,直接在事件处理函数中使用this引用相应的元素。这种直接函数分配可以使用addeventListener方法或通过onclick等传统事件注册方法完成。
  • 类似地,当您直接在元素的事件属性(如<button onclick="...this..." >)中使用this时,它引用了元素。
  • 然而,通过在事件处理函数或事件属性内部调用的其他函数间接使用this会解析为全局对象window
  • 当我们使用Microsoft的事件注册模型方法attachEvent将函数附加到事件处理程序时,也会实现上述行为。它不是将函数分配给事件处理程序(从而使元素的函数方法),而是调用事件上的函数(有效地在全局上下文中调用它)。

我建议最好在JSFiddle中尝试这个

<script>function clickedMe() {alert(this + " : " + this.tagName + " : " + this.id);}document.getElementById("button1").addEventListener("click", clickedMe, false);document.getElementById("button2").onclick = clickedMe;document.getElementById("button5").attachEvent('onclick', clickedMe);</script>
<h3>Using `this` "directly" inside event handler or event property</h3><button id="button1">click() "assigned" using addEventListner() </button><br /><button id="button2">click() "assigned" using click() </button><br /><button id="button3" onclick="alert(this+ ' : ' + this.tagName + ' : ' + this.id);">used `this` directly in click event property</button>
<h3>Using `this` "indirectly" inside event handler or event property</h3><button onclick="alert((function(){return this + ' : ' + this.tagName + ' : ' + this.id;})());">`this` used indirectly, inside function <br /> defined & called inside event property</button><br />
<button id="button4" onclick="clickedMe()">`this` used indirectly, inside function <br /> called inside event property</button> <br />
IE only: <button id="button5">click() "attached" using attachEvent() </button>

8. ES6箭头函数中的#0

在箭头函数中,this将表现得像公共变量:它将从其词法范围继承。定义箭头函数的函数的this将是箭头函数的this

因此,这与以下行为相同:

(function(){}).bind(this)

请参阅以下代码:

const globalArrowFunction = () => {return this;};
console.log(globalArrowFunction()); //window
const contextObject = {method1: () => {return this},method2: function(){return () => {return this};}};
console.log(contextObject.method1()); //window
const contextLessFunction = contextObject.method1;
console.log(contextLessFunction()); //window
console.log(contextObject.method2()()) //contextObject
const innerArrowFunction = contextObject.method2();
console.log(innerArrowFunction()); //contextObject

JavaScript中的this总是指被处决函数的“所有者”。

如果未定义显式所有者,则引用最顶部的所有者,即窗口对象。

所以如果我做了

function someKindOfFunction() {this.style = 'foo';}

element.onclick = someKindOfFunction;

this将引用元素对象。但要小心,很多人都会犯这个错误。

<element onclick="someKindOfFunction()">

在后一种情况下,您只是引用函数,而不是将其交给元素。因此,this将引用窗口对象。

this是JavaScript中被误解的概念之一,因为它的行为因地而异。简单地说,this指的是我们当前正在执行的函数的“所有者”

this有助于获取我们使用的当前对象(又名执行上下文)。如果您了解当前函数在哪个对象中执行,您就可以轻松理解当前this是什么

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 objectconsole.log(obj.innerMethod()()); //returns window.val
//Breakdown in to 2 lines explains this in detailvar _inn = obj.innerMethod();console.log(_inn()); //returns window.val
console.log(obj.outerMethod()); //returns obj.val

上面我们创建了3个同名“val”的变量。一个在全局上下文中,一个在obj中,另一个在obj的innerLaw中。JavaScript通过从本地去全局的范围链来解析特定上下文中的标识符。


#0可以区分的地方很少

调用对象的方法

var status = 1;var helper = {status : 2,getStatus: function () {return this.status;}};
var theStatus1 = helper.getStatus(); //line1console.log(theStatus1); //2
var theStatus2 = helper.getStatus;console.log(theStatus2()); //1

当line1执行时,JavaScript为函数调用建立一个执行上下文(EC),将this设置为最后一个“”之前出现的任何对象引用的对象。。所以在最后一行中,您可以理解a()是在全局上下文中执行的,即window

与构造

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

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

当执行newPerson()时,将创建一个全新的对象。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

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

使用事件处理程序

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

<script type="application/javascript">function click_handler() {alert(this); // alerts the window object}</script>
<button id='thebutton' onclick='click_handler()'>Click me!</button>

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


javascript的this

简单的函数调用

考虑以下函数:

function foo() {console.log("bar");console.log(this);}foo(); // calling the function

请注意,我们在正常模式下运行它,即不使用严格模式。

在浏览器中运行时,this的值将记录为window。这是因为window是Web浏览器范围内的全局变量。

如果您在node.js这样的环境中运行相同的代码,this将引用应用程序中的全局变量。

现在,如果我们在严格模式下通过将语句"use strict";添加到函数声明的开头来运行它,this将不再引用任何环境中的全局变量。这样做是为了避免在严格模式下混淆。this在这种情况下只会记录undefined,因为它就是这样,它没有定义。

在下面的例子中,我们将看到如何操作this的值。

在对象上调用函数

有不同的方法可以做到这一点。如果您在Javascript中调用过forEachslice等本机方法,您应该已经知道在这种情况下this变量指的是您调用该函数的Object(请注意,在Javascript中,几乎所有内容都是Object,包括Arrays和Functions)。以以下代码为例。

var myObj = {key: "Obj"};myObj.logThis = function () {// I am a methodconsole.log(this);}myObj.logThis(); // myObj is logged

如果Object包含包含Function的属性,则该属性称为方法。调用此方法时,将始终将其this变量设置为与之关联的Object。严格和非严格模式都是如此。

请注意,如果一个方法存储(或者更确切地说,复制)在另一个变量中,对this的引用将不再保留在新变量中。例如:

// continuing with the previous code snippet
var myVar = myObj.logThis;myVar();// logs either of window/global/undefined based on mode of operation

考虑一个更常见的实际场景:

var el = document.getElementById('idOfEl');el.addEventListener('click', function() { console.log(this) });// the function called by addEventListener contains this as the reference to the element// so clicking on our element would log that element itself

new关键字

考虑Javascript中的构造函数:

function Person (name) {this.name = name;this.sayHello = function () {console.log ("Hello", this);}}
var awal = new Person("Awal");awal.sayHello();// In `awal.sayHello`, `this` contains the reference to the variable `awal`

这是如何工作的?好吧,让我们看看当我们使用new关键字时会发生什么。

  1. 使用new关键字调用函数将立即初始化Person类型的Object
  2. Object的构造函数将其构造函数设置为Person。另外,请注意typeof awal只会返回Object
  3. 这个新的Object将被分配给Person.prototype的原型。这意味着Person原型中的任何方法或属性都可用于Person的所有实例,包括awal
  4. 现在调用函数Person本身;this是对新构造的对象awal的引用。

很简单,嗯?

请注意,官方的ECMAScript规范没有说明此类类型的函数是实际的constructor函数。它们只是普通函数,new可以用于任何函数。只是我们这样使用它们,所以我们只这样称呼它们。

在函数上调用函数:callapply

所以是的,因为function也是Objects(事实上Javascript中的第一类变量),即使函数也有方法……嗯,函数本身。

所有函数都继承自全局Function,其中两个方法是callapply,两者都可用于操作调用它们的函数中this的值。

function foo () { console.log (this, arguments); }var thisArg = {myObj: "is cool"};foo.call(thisArg, 1, 2, 3);

这是使用call的典型示例。它基本上接受第一个参数并将函数foo中的this设置为对thisArg的引用。传递给call的所有其他参数都作为参数传递给函数foo
所以上面的代码将在控制台中记录{myObj: "is cool"}, [1, 2, 3]。在任何函数中更改this的值都是非常好的方法。

applycall几乎相同,接受它只需要两个参数:thisArg和一个包含要传递给函数的参数的数组。所以上面的call调用可以像这样翻译成apply

foo.apply(thisArg, [1,2,3])

请注意,callapply可以覆盖我们在第二个项目符号中讨论的点方法调用设置的this的值。很简单:)

介绍……bind

bindcallapply的兄弟。它也是Javascript中所有函数从全局Function构造函数继承的方法。bindcall/apply的区别在于callapply实际上都会调用该函数。另一方面,bind返回一个预先设置了call0和call1的新函数。让我们举个例子来更好地理解这一点:

function foo (a, b) {console.log (this, arguments);}var thisArg = {myObj: "even more cool now"};var bound = foo.bind(thisArg, 1, 2);console.log (typeof bound); // logs `function`console.log (bound);/* logs `function () { native code }` */
bound(); // calling the function returned by `.bind`// logs `{myObj: "even more cool now"}, [1, 2]`

看到这三者之间的区别了吗?这很微妙,但它们的使用方式不同。与callapply一样,bind也会覆盖点方法调用设置的this的值。

请注意,这三个函数都不会对原始函数进行任何更改。callapply将返回新构造函数的值,而bind将返回新构造的函数本身,准备好被调用。

额外的东西,收到

有时,您不喜欢this随范围而变化,尤其是嵌套范围。请看下面的示例。

var myObj = {hello: function () {return "world"},myMethod: function () {// copy this, variable names are case-sensitivevar that = this;// callbacks ftw \o/foo.bar("args", function () {// I want to call `hello` herethis.hello(); // error// but `this` references to `foo` damn!// oh wait we have a backup \o/that.hello(); // "world"});}};

在上面的代码中,我们看到this的值随着嵌套范围的变化而变化,但我们想要原始范围中this的值。所以我们将this复制到that并使用副本而不是this。聪明吧?

索引:

  1. 默认情况下this中包含什么?
  2. 如果我们使用对象点表示法将函数调用为方法怎么办?
  3. 如果我们使用new关键字呢?
  4. 我们如何用callapply操作this
  5. 使用bind
  6. 复制this以解决嵌套范围问题。

由于这个线程已经出现,我为刚接触#0主题的读者编写了几点。

this的值是如何确定的?

我们使用这个词的方式类似于我们在英语等自然语言中使用代词的方式:“John跑得很快,因为试图赶上火车。”相反,我们可以写“…John试图赶上火车”。

var person = {firstName: "Penelope",lastName: "Barrymore",fullName: function () {
// We use "this" just as in the sentence above:console.log(this.firstName + " " + this.lastName);
// We could have also written:console.log(person.firstName + " " + person.lastName);}}

this没有赋值直到对象调用定义它的函数。在全局范围内,所有全局变量和函数都在window对象上定义。因此,全局函数中的this引用(并具有)全局window对象。

use strict时,全局函数和未绑定到任何对象的匿名函数中的this的值为undefined

this关键字是最被误解:1)我们借用一个使用this的方法,2)我们将一个使用this的方法分配给一个变量,3)一个使用this的函数作为回调函数传递,4)this在闭包内部使用-一个内部函数。

table

什么拥有未来

ECMA脚本6中定义,arrow-函数采用this绑定封闭(函数或全局)范围。

function foo() {// return an arrow functionreturn (a) => {// `this` here is lexically inherited from `foo()`console.log(this.a);};}var obj1 = { a: 2 };var obj2 = { a: 3 };
var bar = foo.call(obj1);bar.call( obj2 ); // 2, not 3!

虽然箭头函数提供了使用bind()的替代方案,但重要的是要注意它们本质上是禁用传统的this机制,以支持更广泛理解的词法范围。


参考资料:

  1. this&对象原型,作者:Kyle Simpson.©2014 Getify Solutions.
  2. javascriptissexy.comhttp://goo.gl/pvl0GX
  3. Angus Croll-http://goo.gl/Z2RacU

这里JavaScriptthis的一个很好的来源。

以下是摘要:

  • 今年全球

    在浏览器中,在全局范围内,thiswindow对象

    <script type="text/javascript">console.log(this === window); // truevar foo = "bar";console.log(this.foo); // "bar"console.log(window.foo); // "bar"

    在使用repl的node中,this是顶部命名空间。您可以将其称为global

    >this{ ArrayBuffer: [Function: ArrayBuffer],Int8Array: { [Function: Int8Array] BYTES_PER_ELEMENT: 1 },Uint8Array: { [Function: Uint8Array] BYTES_PER_ELEMENT: 1 },...>global === thistrue

    在从脚本执行的node中,全局范围内的this以空对象开始。它与global不同

    \\test.jsconsole.log(this);  \\ {}console.log(this === global); \\ fasle
  • function this

Except in the case of DOM event handlers or when a thisArg is provided (see further down), both in node and in a browser using this in a function that is not called with new references the global scope…

<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;,在这种情况下this将是undefined

<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将是一个新的上下文,它不会引用全局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>
  • 原型这个

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

function Thing() {console.log(this.foo);}
Thing.prototype.foo = "bar";
var thing = new Thing(); //logs "bar"console.log(thing.foo);  //logs "bar"

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

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 []
  • 反对这

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

var obj = {foo: "bar",logFoo: function () {console.log(this.foo);}};
obj.logFoo(); //logs "bar"
  • DOM事件

在超文本标记语言DOM事件处理程序中,this始终是对事件附加到的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();

除非你是bind

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();
  • 超文本标记语言this

在可以放置JavaScript的超文本标记语言属性中,this是对元素的引用。

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

您可以使用eval访问this

function Thing () {}Thing.prototype.foo = "bar";Thing.prototype.logFoo = function () {eval("console.log(this.foo)"); //logs "bar"}
var thing = new Thing();thing.logFoo();
  • 与此

您可以使用withthis添加到当前范围以读取和写入this上的值,而无需显式引用this

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这个

jQuery在很多地方都会有this引用一个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>

这样使用Scope就像这样

  <script type="text/javascript" language="javascript">$('#tbleName tbody tr').each(function{var txt='';txt += $(this).find("td").eq(0).text();\\same as above but synatx differentvar txt1='';txt1+=$('#tbleName tbody tr').eq(0).text();alert(txt1)});</script>

txt1和txt的值相同在上例中$(this)=$('#tbleName tbody tr')相同

这是我见过的最好的解释:使用Clarity理解JavaScriptthis

这个引用始终引用(并保存值)一个对象-一个单一的对象-它通常用于函数或方法,尽管它可以在全局函数之外使用范围。请注意,当我们使用严格模式时,这保存了未定义的全局函数和未定义的匿名函数绑定到任何对象。

有四种情况这个可能会令人困惑:

  1. 当我们传递一个方法(使用这个)作为参数用作回调函数时。
  2. 当我们使用内部函数(闭包)时。重要的是要注意闭包不能通过使用this关键字访问外部函数的这个变量,因为this变量只能由函数本身访问,而不能由内部函数访问。
  3. 当依赖于这个的方法被分配给跨上下文的变量时,在这种情况下,这个引用了另一个超出最初预期的对象。
  4. 当使用这个以及bind、应用和调用方法时。

他给出了代码示例、解释和解决方案,我认为这很有帮助。

关于this的最详细和最全面的文章如下:

JavaScript中'this'关键字的温和解释

this背后的想法是理解函数调用类型对设置this值具有重要意义。


当遇到识别this的麻烦时,不要问问自己:

#0取自在哪里?

问问自己:

函数援引是什么?

对于箭头函数(上下文透明的特殊情况),问问自己:

箭头函数定义的值this是什么?

这种心态在处理this时是正确的,会让你免于头痛。

关于如何在JavaScript中解释“这个”关键字有很多困惑。希望这篇文章能一劳永逸地解决所有这些问题。还有更多。请仔细阅读整篇文章。事先警告,这篇文章很长。

无论使用它的上下文如何,“这个”总是引用Javascript中的“当前对象”。然而,“当前对象”的内容与背景不同。背景可能正好是6中的1以下:

  1. 全球(即在所有函数之外)
  2. 内部直接“非绑定函数”调用(即通过调用functionName.bind绑定没有被的函数)
  3. 内部间接“非绑定函数”调用functionName.callfunctionName.apply
  4. 在“绑定函数”调用中(即已经绑定通过调用functionName.bind的函数)
  5. 通过“新”创建对象
  6. 内部内联DOM事件处理程序

以下将逐一描述每个上下文:

  1. 全球背景(即在所有函数之外):

    在所有函数之外(即在全局上下文中)"当前对象“(因此“这个”的值)始终是浏览器的“windows”对象。

  2. 内部直接“非绑定函数”调用

    在直接的“非绑定函数”调用中,对象调用的函数调用成为“当前对象”(因此“这个”的值)。如果在没有显式当前对象的情况下调用函数,则当前对象"窗口"对象(对于非严格模式)或未定义(对于严格模式)。中定义的任何函数(或变量)全局上下文自动成为"窗口"对象的属性。例如,假设函数在全局上下文中定义为

    function UserDefinedFunction(){alert(this)}

    它成为窗口对象的属性,就好像您定义了作为

    window.UserDefinedFunction=function(){alert(this)}

    在“非严格模式”下,直接通过"用户定义函数()"调用/调用此函数将自动调用/调用“用户自定义函数”中的“当前对象”(因此是的值)。在“非严格模式”中调用此函数将导致以下结果

    UserDefinedFunction() // displays [object Window]  as it automatically gets invoked as window.UserDefinedFunction()

    在“严格模式”下,直接通过以下方式调用/调用函数"用户定义函数()"“不是”自动调用/调用它作为"窗口。用户定义的函数()"。因此”当前对象“(和“这个”的值)在“UserCustedFunction”应为未定义。在“严格模式”下调用此函数将导致以下结果

    UserDefinedFunction() // displays undefined

    但是,使用窗口对象显式调用它将导致以下

    window.UserDefinedFunction() // "always displays [object Window]   irrespective of mode."

    让我们看另一个例子。请查看以下代码

     function UserDefinedFunction(){alert(this.a + ","  + this.b + ","  + this.c  + ","  + this.d)}
    var o1={a:1,b:2,f:UserDefinedFunction}var o2={c:3,d:4,f:UserDefinedFunction}
    o1.f() // Shall display 1,2,undefined,undefinedo2.f() // Shall display undefined,undefined,3,4

    在上面的例子中,我们看到当“用户自定义函数”是通过o1调用,“这个”o1的值将显示其属性"一个""b"的值"c""d"显示为未定义o1没有定义这些属性

    类似地,当通过o2调用“用户自定义函数”时,"一个""b"的值显示为未定义,因为o2没有定义这些属性。

  3. 内部间接“非绑定函数”调用functionName.callfunctionName.apply

    当一个“非绑定函数”被调用时

    function UserDefinedFunction(){alert(this.a + ","  + this.b + ","  + this.c  + ","  + this.d)}var o1={a:1,b:2,f:UserDefinedFunction}var o2={c:3,d:4,f:UserDefinedFunction}
    UserDefinedFunction.call(o1) // Shall display 1,2,undefined,undefinedUserDefinedFunction.apply(o1) // Shall display 1,2,undefined,undefined
    UserDefinedFunction.call(o2) // Shall display undefined,undefined,3,4UserDefinedFunction.apply(o2) // Shall display undefined,undefined,3,4
    o1.f.call(o2) // Shall display undefined,undefined,3,4o1.f.apply(o2) // Shall display undefined,undefined,3,4
    o2.f.call(o1) // Shall display 1,2,undefined,undefinedo2.f.apply(o1) // Shall display 1,2,undefined,undefined
    "this"参数没有显式传递给(因此"this"的值)在非严格模式下设置为"窗口",在严格模式下设置为“未定义”

  4. 在“绑定函数”调用中(即通过调用functionName.bind绑定的函数):

    绑定函数是一个函数,其“这个”值已被修复。以下代码演示了“这个”在case中的工作原理的绑定函数

    function UserDefinedFunction(){alert(this.a + ","  + this.b + ","  + this.c  + ","  + this.d)}var o1={a:1,b:2,f:UserDefinedFunction,bf:null}var o2={c:3,d:4,f:UserDefinedFunction,bf:null}
    var bound1=UserDefinedFunction.bind(o1); // permanantly fixes "this" value of function "bound1" to Object o1bound1() // Shall display 1,2,undefined,undefined
    var bound2=UserDefinedFunction.bind(o2); // permanantly fixes "this" value of function "bound2" to Object o2bound2() // Shall display undefined,undefined,3,4
    var bound3=o1.f.bind(o2); // permanantly fixes "this" value of function "bound3" to Object o2bound3() // Shall display undefined,undefined,3,4
    var bound4=o2.f.bind(o1); // permanantly fixes "this" value of function "bound4" to Object o1bound4() // Shall display 1,2,undefined,undefined
    o1.bf=UserDefinedFunction.bind(o2) // permanantly fixes "this" value of function "o1.bf" to Object o2o1.bf() // Shall display undefined,undefined,3,4
    o2.bf=UserDefinedFunction.bind(o1) // permanantly fixes "this" value of function "o2.bf" to Object o1o2.bf() // Shall display 1,2,undefined,undefined
    bound1.call(o2) // Shall still display 1,2,undefined,undefined. "call" cannot alter the value of "this" for bound function
    bound1.apply(o2) // Shall still display 1,2,undefined,undefined. "apply" cannot alter the value of "this" for bound function
    o2.bf.call(o2) // Shall still display 1,2,undefined,undefined. "call" cannot alter the value of "this" for bound functiono2.bf.apply(o2) // Shall still display 1,2,undefined,undefined."apply" cannot alter the value of "this" for bound function

    如上面的代码所示,任何“绑定函数”的“this”值不能通过调用/应用进行更改。此外,如果“这个”参数未显式传递给绑定,“当前对象”(因此“这个”的值)在非中设置为"窗口"严格模式和“未定义”在严格模式。还有一件事。绑定已经绑定的函数不会改变“这个”的值。它仍然设置为第一个绑定函数设置的值。

  5. 通过“新”创建对象

    在构造函数内部,“当前对象”(因此是“这个”)引用当前正在创建的对象通过“新”而不管函数的绑定状态。然而如果构造函数是一个绑定函数,它将被调用为绑定函数设置的预定义参数集。

  6. 内部内联DOM事件处理程序

    请查看以下超文本标记语言片段

    <button onclick='this.style.color=white'>Hello World</button><div style='width:100px;height:100px;' onclick='OnDivClick(event,this)'>Hello World</div>

    上述示例中的“这个”表示“按钮”元素和“div”元素。

    在第一个示例中,按钮的字体颜色应设置为点击时为白色。

    在第二个示例中,当单击"div"元素时,它将用第二个参数调用OnDivClick函数引用单击的div元素。但是“这个”的值“未定义”分别在不严格严格模式中(如果OnDivClick未绑定函数)或设置为预定义绑定值(如果OnDivClick界函数

下面总结了整篇文章

  1. 在全局上下文中"this"总是引用"windows"对象

  2. 每当调用函数时,它都会在对象(“当前对象”)。如果没有显式提供当前对象当前对象NON严格中的"窗口对象"默认情况下,严格模式下的模式“未定义”

  3. 非绑定函数中"this"的值是对调用函数的上下文对象的引用("当前对象"

  4. 非绑定函数中“这个”的值可以通过以下方式覆盖调用函数的适用方法。

  5. “这个”的值对于绑定函数是固定的,不能被函数的打电话适用方法覆盖。

  6. 绑定和已绑定函数不会更改“this”的值。它仍然设置为第一个绑定函数设置的值。

  7. 构造函数中“这个”的值是正在执行的对象创建并初始化

  8. 内联DOM事件处理程序中“这个”的值是引用到给定事件处理程序的元素。

“this”的值取决于执行函数的“上下文”。上下文可以是任何对象或全局对象,即窗口。

所以“this”的语义与传统的OOP语言不同。它会导致问题:1.当函数传递给另一个变量(很可能是回调)时;2.当从类的成员方法调用闭包时。

在这两种情况下,这都设置为窗口。

用伪古典术语来说,许多讲座教授'this'关键字的方式是作为一个由类或对象构造函数实例化的对象。每次从类构造一个新对象时,想象在引擎盖下创建并返回一个'this'对象的本地实例。我记得它是这样教的:

function Car(make, model, year) {var this = {}; // under the hood, so to speakthis.make = make;this.model = model;this.year = year;return this; // under the hood}
var mycar = new Car('Eagle', 'Talon TSi', 1993);// ========= under the hoodvar this = {};this.make = 'Eagle';this.model = 'Talon TSi';this.year = 1993;return this;

关于这个关键字的一点信息

让我们在全局范围内将this关键字记录到控制台,而无需更多代码,但

console.log(this)

客户端/浏览器this关键字是window的全局对象

console.log(this === window) // true

服务器/节点/Javascript运行时this关键字也是全局对象module.exports

console.log(this === module.exports) // trueconsole.log(this === exports) // true

请记住exports只是对module.exports的引用

摘要this Javascript:

  • this的值由函数是如何调用的,它是在哪里创建的!决定
  • 通常this的值由点左侧的Object确定。(全局空间中的window
  • 在事件侦听器中,this的值指的是调用事件的DOM元素。
  • 当使用new关键字调用in函数时,this的值指的是新创建的对象
  • 您可以使用以下函数操作this的值:callapplybind

示例:

let object = {prop1: function () {console.log(this);}}
object.prop1();   // object is left of the dot, thus this is object
const myFunction = object.prop1 // We store the function in the variable myFunction
myFunction(); // Here we are in the global space// myFunction is a property on the global object// Therefore it logs the window object              

事件监听器示例:

document.querySelector('.foo').addEventListener('click', function () {console.log(this);   // This refers to the DOM element the eventListener was invoked from})

document.querySelector('.foo').addEventListener('click', () => {console.log(this);  // Tip, es6 arrow function don't have their own binding to the this v})                    // Therefore this will log the global object
.foo:hover {color: red;cursor: pointer;}
<div class="foo">click me</div>

Example constructor:

function Person (name) {this.name = name;}
const me = new Person('Willem');// When using the new keyword the this in the constructor function will refer to the newly created object
console.log(me.name);// Therefore, the name property was placed on the object created with new keyword.

我对this的看法与其他答案不同,我希望能有所帮助。

查看JavaScript的一种方法是查看只有一种方法可以调用函数1。它是

functionObject.call(objectForThis, arg0, arg1, arg2, ...);

总是为objectForThis提供一些值。

其他一切都是functionObject.call的语法糖

所以,其他一切都可以通过它如何翻译成functionObject.call来描述。

如果你只是调用一个函数,那么this是“全局对象”,在浏览器中是窗口

function foo() {console.log(this);}
foo();  // this is the window object

换句话说,

foo();

被有效地转化为

foo.call(window);

请注意,如果您使用严格模式,那么this将是undefined

'use strict';
function foo() {console.log(this);}
foo();  // this is the window object

这意味着

换句话说,

foo();

被有效地转化为

foo.call(undefined);

在JavaScript中,有+-*等运算符。还有.的点操作符

当与右边的函数和左边的对象一起使用时,.运算符实际上意味着“将对象作为this传递给函数。

示例

const bar = {name: 'bar',foo() {console.log(this);},};
bar.foo();  // this is bar

换句话说,bar.foo()转化为const temp = bar.foo; temp.call(bar);

请注意,函数是如何创建的并不重要(主要是…)。所有这些都会产生相同的结果

const bar = {name: 'bar',fn1() { console.log(this); },fn2: function() { console.log(this); },fn3: otherFunction,};
function otherFunction() { console.log(this) };
bar.fn1();  // this is barbar.fn2();  // this is barbar.fn3();  // this is bar

再一次,这些都只是句法糖

{ const temp = bar.fn1; temp.call(bar); }{ const temp = bar.fn2; temp.call(bar); }{ const temp = bar.fn3; temp.call(bar); }

另一个问题是原型链。当你使用a.b JavaScript时,首先在a直接引用的对象上查找属性b。如果在对象上找不到b,那么JavaScript将在对象的原型中查找b

有多种方法来定义对象的原型,2019年最常见的是class关键字。对于this来说,虽然无关紧要。重要的是,如果它在对象a中查找属性b,如果它在对象上找到属性b,或者如果b最终是一个函数,那么在它的原型链中查找属性b,那么适用上述相同的规则。函数b引用将使用call方法调用,并将a作为ObjectForThis传递,如本答案的顶部所示。

现在。让我们假设我们创建一个函数,在调用另一个函数之前显式设置this,然后使用.(点)运算符调用它

function foo() {console.log(this);}
function bar() {const objectForThis = {name: 'moo'}foo.call(objectForThis);  // explicitly passing objectForThis}
const obj = {bar,};
obj.bar();  

在转换为使用call之后,obj.bar()变成了const temp = obj.bar; temp.call(obj);。当我们输入bar函数时,我们调用foo,但我们显式地传入了另一个对象用于对象ForThis,因此当我们到达foothis时,该对象是内部对象。

这就是bind=>函数有效地做的事情。它们更像语法糖。它们有效地构建了一个新的不可见函数,就像上面的bar一样,在调用任何指定的函数之前显式设置this。在bind的情况下,this被设置为您传递给bind的任何内容。

function foo() {console.log(this);}
const bar = foo.bind({name: 'moo'});
// bind created a new invisible function that calls foo with the bound object.
bar();
// the objectForThis we are passing to bar here is ignored because// the invisible function that bind created will call foo with with// the object we bound above
bar.call({name: 'other'});

请注意,如果functionObject.bind不存在,我们可以像这样制作自己的

function bind(fn, objectForThis) {return function(...args) {return fn.call(objectForthis, ...args);};}

然后我们可以这样称呼它

function foo() {console.log(this);}
const bar = bind(foo, {name:'abc'});

箭头函数,=>运算符是bind的语法糖

const a = () => {console.log(this)};

const tempFn = function() {console.log(this)};const a = tempFn.bind(this);

就像bind一样,创建了一个新的不可见函数,它使用objectForThis的绑定值调用给定的函数,但与bind不同,要绑定的对象是隐式的。当使用=>运算符时,它就是this

就像上面的规则一样

const a = () => { console.log(this); }  // this is the global object
'use strict';const a = () => { console.log(this); }  // this is undefined
function foo() {return () => { console.log(this); }}
const obj = {foo,};const b = obj.foo();b();

obj.foo()转换为const temp = obj.foo; temp.call(obj);,这意味着foo中的箭头运算符将obj绑定到一个新的不可见函数,并返回分配给b的新不可见函数。b()将像b.call(window)b.call(undefined)一样工作,调用foo创建的新不可见函数。该不可见函数忽略传递给它的this,并将obj作为ObjectForThis'传递给箭头函数。

上面的代码翻译为

function foo() {function tempFn() {console.log(this);}return tempFn.bind(this);}
const obj = {foo,};const b = obj.foo();b.call(window or undefined if strict mode);

1apply是另一个类似于call的函数

functionName.apply(objectForThis, arrayOfArgs);

但从概念上讲,ES6甚至可以将其转化为

functionName.call(objectForThis, ...arrayOfArgs);

要正确理解“这个”,必须理解它们之间的上下文、范围和区别。

范围:在javascript中作用域与变量的可见性有关,作用域通过使用函数来实现。(阅读更多关于作用域的信息)

背景:上下文与对象相关。它指的是函数所属的对象。当您使用JavaScript“this”关键字时,它指的是函数所属的对象。例如,在函数内部,当您说:“this.accoutNumber”时,您指的是属于该函数所属对象的属性“accoutNumber”。

如果对象“myObj”有一个名为“getMyName”的方法,当JavaScript关键字“this”在“getMyName”中使用时,它指的是“myObj”。如果函数“getMyName”在全局范围内执行,那么“this”指的是窗口对象(严格模式除外)。

现在让我们看一些例子:

    <script>console.log('What is this: '+this);console.log(this);</script>

在浏览器输出中运行上面的代码将:输入图片描述

根据您在窗口对象上下文中的输出,窗口原型引用对象也是可见的。

现在让我们在函数内部尝试:

    <script>function myFunc(){console.log('What is this: '+this);console.log(this);}myFunc();</script>

输出:

在此处输入图片描述输出是相同的,因为我们将“this”变量记录在全局范围内,并将其记录在函数范围内,我们没有更改上下文。在这两种情况下,上下文都是相同的,与寡妇对象相关。

现在让我们创建我们自己的对象。在javascript中,您可以通过多种方式创建对象。

 <script>var firstName = "Nora";var lastName = "Zaman";var myObj = {firstName:"Lord",lastName:'Baron',printNameGetContext:function(){console.log(firstName + " "+lastName);console.log(this.firstName +" "+this.lastName);return this;}}
var context = myObj.printNameGetContext();console.log(context);</script>

输出:输入图片描述

所以从上面的例子中,我们发现'this'关键字指的是一个与myObj相关的新上下文,myObject也有Object的原型链。

让我们再举一个例子:

<body><button class="btn">Click Me</button><script>function printMe(){//Terminal2: this function declared inside window context so this function belongs to the window object.console.log(this);}document.querySelector('.btn').addEventListener('click', function(){//Terminal1: button context, this callback function belongs to DOM elementconsole.log(this);printMe();})</script></body>

输出:有意义吗?(阅读评论)输入图片描述

如果您无法理解上面的示例,让我们尝试使用我们自己的回调;

<script>var myObj = {firstName:"Lord",lastName:'Baron',printName:function(callback1, callback2){//Attaching callback1 with this myObj contextthis.callback1 = callback1;this.callback1(this.firstName +" "+this.lastName)//We did not attached callback2 with myObj so, it's reamin with window context by defaultcallback2();/*//test bellow codesthis.callback2 = callback2;this.callback2();*/}}var callback2 = function (){console.log(this);}myObj.printName(function(data){console.log(data);console.log(this);}, callback2);</script>

输出:输入图片描述

现在让我们了解范围,自我,生活以及这是如何表现的

       var color = 'red'; // property of windowvar obj = {color:'blue', // property of windowprintColor: function(){ // property of obj, attached with objvar self = this;console.log('In printColor -- this.color: '+this.color);console.log('In printColor -- self.color: '+self.color);(function(){ // decleard inside of printColor but not property of object, it will executed on window context.console.log(this)console.log('In IIFE -- this.color: '+this.color);console.log('In IIFE -- self.color: '+self.color);})();
function nestedFunc(){// decleard inside of printColor but not property of object, it will executed on window context.console.log('nested fun -- this.color: '+this.color);console.log('nested fun -- self.color: '+self.color);}
nestedFunc(); // executed on window contextreturn nestedFunc;}};
obj.printColor()(); // returned function executed on window context</script>

输出非常棒,对吧?输入图片描述

JavaScript中的“this”这是执行上下文的属性之一。输入图片描述

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

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

1.在方法内部。

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

输入图片描述在上面的示例中,方法“fullName()”由对象“人”调用,因此方法“fullName()”中this的值将等于“人”对象。

2.在函数内部。

i)函数声明/表达式

在松散模式下,this===窗口(对象)输入图片描述

在严格模式下,这===未定义输入图片描述

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

ii)箭头功能:

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

输入图片描述

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

输入图片描述

在上面的例子中,由于单击处理程序附加到“文档”对象,这将等于“文档”对象

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

调用()和应用()方法是预定义的JavaScript方法。

它们都可用于调用具有另一个对象作为参数的对象方法。

输入图片描述

在上面的示例中,“printFull详细信息()”中的this通过作为调用方法的第一个参数传递而显式设置为人员对象1和人员对象2。

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