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

How does the "this" keyword work, and when should it be used?

提问人:Maxim Gershkovich 提问时间:6/27/2010 最后编辑:JΛYDΞVMaxim Gershkovich 更新时间:3/16/2023 访问量:462358

问:

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

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

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

javascript 这个

评论

1赞 Paul Dixon 6/27/2010
相关jQuery/JavaScript“this”指针混淆在Javascript中,为什么“this”运算符不一致?
0赞 Sebastian Simon 6/21/2021
相关新闻: “箭头函数”和“函数”是否等同/可互换?

答:

15赞 blockhead 6/27/2010 #1

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

  1. 函数的调用方式(包括作为对象方法、调用应用的使用、函数的使用))
  2. bind 的使用
  3. 箭头函数的词法(它们采用其外部执行上下文的 this
  4. 代码是严格模式还是非严格模式
  5. 是否使用 eval 调用了代码

您可以使用 或 设置此值。func.callfunc.applyfunc.bind

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

jQuery使使用jQuery.proxy进行更改变得微不足道。

评论

9赞 Pointy 6/27/2010
更正确的说法是,每个函数调用都有一个作用域。换句话说,Javascript 中令人困惑的是,它不是函数本身的内在属性,而是函数调用方式的产物。this
0赞 Sushil 6/25/2013
@pointy谢谢。在 JS 中对此最令人困惑的是,在之前使用的所有语言(C#、C++)中, - 这不能作 n 总是指向对象实例,而在 js 中,它依赖于并且可以在调用函数时更改 ,等等。func.callfunc.bind
2赞 nnnnnn 1/4/2015
this引用函数的作用域。 将引用一个特定的对象(或可能),正如您所说,可以使用 或 进行更改。函数的作用域是(本质上,简化后)它可以访问哪些变量,这完全取决于函数的声明位置,并且无法更改。thisundefined.call().apply()
0赞 T.J. Crowder 11/14/2015
@Pointy:“说每个函数调用都有一个作用域更正确一些。更正确的说法是,函数(现在是块)有作用域,函数调用上下文。作用域定义该作用域中的代码可以使用的标识符。上下文定义了这些标识符绑定到的内容。
1赞 T.J. Crowder 11/14/2015
“无论这个范围是什么,都由'这个'引用。”不,在 ES5 和之前(例如,当写这个答案时)和范围没有任何关系。在 ES2015(又名 ES6)中,和 scope 以一种相当小的方式与箭头函数相关(in an arrow 函数继承自其封闭范围),但从不引用范围。thisthisthisthis
1490赞 35 revs, 9 users 70%Sebastian Simon #2

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

规格中的技术说明this

ECMAScript 标准通过抽象操作(缩写为 AOResolveThisBinding 定义了这一点

[AO] ResolveThisBinding [...] 使用正在运行的执行上下文的 LexicalEnvironment 确定关键字的绑定。[步骤]:this

  1. envRecGetThisEnvironment()。
  2. 返回?环境建议。GetThisBinding() 中。

全局环境记录、模块环境记录和函数环境记录各有其自己的 GetThisBinding 方法。

GetThisEnvironment AO 查找当前正在运行的执行上下文的 LexicalEnvironment,并查找最接近的上升环境记录(通过迭代访问其 [[OuterEnv]] 属性),该记录具有 this 绑定(即 HasThisBinding 返回 true)。此过程以三种环境记录类型之一结束。

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

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

您可以将代码示例放在 AST 资源管理器中,以便遵循规范详细信息。

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

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

<script>
// Global context
console.log(this); // Logs global object.

setTimeout(function(){
  console.log("Not global context");
});
</script>

在脚本的初始全局执行上下文中,计算会导致 GetThisBinding 执行以下步骤:this

全局环境记录环境的 GetThisBinding 具体方法 [...][这样做]:

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

全局环境记录的 [[GlobalThisValue]] 属性始终设置为主机定义的全局对象,该对象可通过 globalThis ( 在 Web 上,在 Node.js;MDN 上的文档)。按照 InitializeHostDefinedRealm 的步骤了解 [[GlobalThisValue]] 属性是如何形成的。windowglobal

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

ECMAScript 2015 中引入了模块。

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

在模块的初始全局执行上下文中,评估会导致 GetThisBinding 执行以下步骤:this

模块环境记录的 GetThisBinding 具体方法 [...][这样做]:

  1. 返回 undefined

在模块中,的值始终在全局上下文中。模块隐式处于严格模式thisundefined

3. 输入评估

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

  • 直接呼叫通常看起来像......或。。。(或...等)。1 只有当调用表达式符合窄模式时,它才是直接的。阿拉伯数字evaleval();(eval)();((eval))();
  • 间接调用涉及以任何其他方式调用函数引用。它可能是......、......、...、......等。鉴于 ,它也将是 ..., ....另外,给定,调用...也将是间接的。evalevaleval?.()(, eval)()window.eval()eval.call(,)const aliasEval1 = eval; window.aliasEval2 = eval;aliasEval1()aliasEval2()const originalEval = eval; window.eval = (x) => originalEval(x);eval()

参见 chuckj 对 “(1, eval)('this') vs eval('this') in JavaScript?”的回答,以及 Dmitry Soshnikov 的 ECMA-262-5 – 第 2 章:严格模式已存档),了解何时可以使用间接调用。eval()

PerformEval 执行代码。它创建一个新的声明性环境记录作为其 LexicalEnvironment,这是 GetThisEnvironment 从中获取值的位置。evalthis

然后,如果出现在代码中,则调用 GetThisEnvironment 找到的环境记录的 GetThisBinding 方法并返回其值。thiseval

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

这意味着:

  • 在直接求值中,该值不会更改;它取自名为 .thiseval
  • 在间接计算中,该值是全局对象 ()。thisglobalThis

新功能呢?new 函数类似于 ,但它不会立即调用代码;它创建一个函数。答:此绑定不适用于此处的任何位置,除非调用该函数时,该函数正常工作,如下一个小节所述。eval

4. 输入功能代码

调用函数时会输入函数代码。

调用函数有四类语法。

实际的函数调用发生在 Call AO 上,该 AO 使用从上下文确定的 thisValue 进行调用;此参数在一长串与呼叫相关的调用中传递。 Call 调用函数的 [[Call]] 内部槽。这将调用 PrepareForOrdinaryCall,其中创建了新函数 Environment Record

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

此外,函数 Environment Record 中还有 [[ThisValue]] 字段:

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

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

[[Call]] 还调用 OrdinaryCallBindThis,其中相应的 thisArgument 是根据以下条件确定的:

  • 原始参考,
  • 函数的种类,以及
  • 代码是否处于严格模式

确定后,对新创建的函数 Environment Record 的 BindThisValue 方法的最终调用实际上会将 [[ThisValue]] 字段设置为 thisArgument

最后,这个字段是函数 Environment Record 的 GetThisBinding AO 从中获取的值的位置:this

函数 Environment Record envRec 的 GetThisBinding 具体方法 [...][这样做]:

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

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

箭头函数

计算箭头函数时,函数对象的 [[ThisMode]] 内部槽在 OrdinaryFunctionCreate 中设置为“lexical”。

OrdinaryCallBindThis,它采用函数 F

  1. thisModeF。[[ThisMode]]。
  2. 如果 thisMode词法,则返回 NormalCompletion()。 [...]undefined

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

那么,箭头函数里面是什么呢?回顾 ResolveThisBindingGetThisEnvironment,HasThisBinding 方法显式返回 falsethis

函数 Environment Record envRec 的 HasThisBinding 具体方法 [...][这样做]:

  1. 如果 envRec.[[ThisBindingStatus]] 是词法,返回 false;否则,返回 true

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

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

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

函数属性

在普通函数(,方法)中,由函数的调用方式决定。functionthis

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

请考虑以下包含函数的对象:

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

或者:

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

在以下任何函数调用中,里面的值将是 。1thisfuncrefObj

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

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

而且,每个角色都扮演三个角色:refObj.funcrefObj

  • 它们都是表达式,
  • 它们都是参考,并且
  • 它们都是价值观。

refObj.funcas a value 是可调用的函数对象;相应的引用用于确定绑定。this

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

EvaluateCall 使用该引用的 IsPropertyReference 在语法上确定它是否是对象的属性。它试图获取引用的 [[Base]] 属性(例如,当应用于 ; 或应用于 时)。如果它被写成一个属性,那么 GetThisValue 将获取这个 [[Base]] 属性,并将其用作 this 值。refObjrefObj.funcfoo.barfoo.bar.baz

注意:Getter / Setter 的工作方式与方法相同,关于 .简单属性不会影响执行上下文,例如,这里是全局范围:thisthis

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

没有基本引用、严格模式和

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

func(); // As opposed to `refObj.func();`.

在传递或分配方法或使用逗号运算符时,也会发生这种情况。这就是“参考记录”和“值”之间的区别所在。

注意函数:按照规范,您会注意到只能返回函数对象(Value)本身,而不能返回引用记录。因此,基本引用将丢失。jjrefObj

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 为 undefined 的 Call。这在 OrdinaryCallBindThis 中有所不同(F:函数对象;thisArgument:传递给 CallthisValue):

  1. thisModeF。[[ThisMode]]。

[...]

  1. 如果 thisMode 严格,设 thisValuethisArgument
  2. 埃尔默
    1. 如果 thisArgument 为 undefinednull,则
      1. globalEnvcalleeRealm。[[GlobalEnv]]。
      2. [...]
      3. thisValueglobalEnv。[[GlobalThisValue]]。
    2. 埃尔默
      1. thisValue 成为 !ToObject(thisArgument) 中。
      2. 注意:ToObject 生成包装对象 [...]。

[...]

注意:步骤 5 在严格模式下将实际值设置为提供的 thisArgument — 在本例中。在“草率模式”中,undefined 或 null thisArgument 会导致成为全局 this 值。thisundefinedthis

如果 IsPropertyReference 返回 false,则 EvaluateCall 将执行以下步骤:

  1. refEnvref。[[基地]]。
  2. 断言:refEnv 是环境记录。
  3. thisValuerefEnv。WithBaseObject() 中。

这就是未定义的 thisValue 可能来自的地方:refEnvWithBaseObject() 始终未定义 with 语句除外。在本例中,thisValue 将是绑定对象。

还有 Symbol.unscopablesMDN 上的文档)来控制绑定行为。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

.call、.apply.bindthisArg 和基元

OrdinaryCallBindThis 的第 5 步与第 6.2 步(规范中的 6.b)结合使用的另一个结果是,基元 this仅在“草率”模式下被强制到对象。

为了检查这一点,让我们引入 this 值的另一个来源:重this 绑定的三个方法:4

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

.bind 创建一个绑定函数,其绑定设置为 thisArg,并且无法再次更改。.call.apply 立即调用该函数,并将 this 绑定设置为 thisArg

.call并使用指定的 thisArg 直接映射到 Call。 使用 BoundFunctionCreate 创建绑定函数。它们有自己的 [[Call]] 方法,用于查找函数对象的 [[BoundThis]] 内部插槽。.apply.bind

设置自定义值的示例:

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 将 设置为此处选择的 DOM 元素:this

$("button").click(function(){
  console.log(this); // Logs the clicked button.
});

构造函数、

使用运算符将函数作为构造函数调用时,EvaluateNew 调用 Construct,后者调用 [[Construct]] 方法。如果函数是基构造函数(即不是......),则它将 thisArgument 设置为从构造函数的原型创建的新对象。在构造函数中设置的属性将最终出现在生成的实例对象上。 隐式返回,除非显式返回自己的非基元值。newclass extends{}thisthis

是 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

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

调用 super 会使用调用的词法范围(函数 Environment Record)的 this 值调用 super 构造函数。GetThisValue 对调用有特殊的规则。它使用 BindThisValue 设置为该环境记录。superthis

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

计算 a 时,将执行 ClassDefinitionEvaluation,修改正在运行的执行上下文。对于每个 ClassElementclass

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

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

静态模块目前是 TC39 第 3 阶段的提案。静态块的工作方式与静态字段和方法相同:它们内部指的是类本身。this

请注意,在方法和 getters / setter 中,其工作方式与普通函数属性相同。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:相当于; 等同于 。这在这篇 2ality 文章已存档)中进行了解释。具体要了解如何计算 ParenthesizedExpression(o.f)()o.f()(f)()f()

2:它必须是 MemberExpression,不能是属性,必须具有完全“eval”的 [[ReferencedName]],并且必须是 %eval% 内部对象。

3:每当规范说“让 ref 是计算 X 的结果”时,那么 X 就是您需要找到的计算步骤的表达式。例如,计算 MemberExpressionCallExpression这些算法之一的结果。其中一些会生成参考记录

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


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

对于每个代码片段,回答以下问题:“标记行处的值是什么?为什么?

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

  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.当将函数作为对象的属性调用时,调用该函数时,将 this 绑定设置为引用的基,即 。obj.methodobj

  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.由于函数值 / 不是从对象中调用的,因此作为属性,this 绑定将是 。 这与 Python 不同,在 Python 中,访问方法 () 会创建一个绑定的方法对象myFunobj.myMethodglobalThisobj.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.在评估直接 eval 调用时,是 。但是,在 eval 代码中,不会从对象中调用,因此 this 绑定被设置为全局对象。thisobjmyFun

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

    obj.该行正在调用特殊的内置函数,该函数接受作为第一个参数。myFun.call(obj);Function.prototype.callthisArg

  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());
    
       

    它是 的实例。箭头函数不会更改 this 绑定,因此它来自词法范围。因此,这与上面提到的类字段完全相同,例如 .尝试将其更改为 .你得到你期望的结果吗?MyClsa = this;static arrow

评论

1赞 Sebastian Simon 11/21/2021
另一种常见情况:调用 EventHandlers 时,将 EventHandlers 设置为 Event。这三个建议可以在将来包含:Bind operator :Explicit this,this argument reflection。DOM 0 事件属性 like 也值得注意:JS 代码隐式包装在一个作用域中,一个作用域用于 clicked 元素,造成混淆; 是具有该属性的元素。thiscurrentTargetonclickwithdocumentthis
2赞 Sebastian Simon 2/9/2022
@LRDPRDX 嗯,这个答案包含了 .但是没有人真正需要全局范围,被弃用,被劝阻,严格模式应该在任何地方使用,等等。剩下的就是调用好像是一个或一个方法; 没有任何呼叫;、 和 可用于显式绑定;箭头函数没有绑定。类:在静态事物中,指类本身,在非静态事物中,指正在创建的实例。就是这样。thisthiswithevalobj.method()methodobjthismethodfunctionfunc()functhis.bind.call.applythisthisthis
4赞 Simon Groenewolt 6/27/2010 #3

谁帮了忙?(javascript 中对“this”的大多数混淆来自这样一个事实,即它通常不链接到你的对象,而是链接到当前的执行范围——这可能不完全是它的工作方式,但对我来说总是这样——请参阅文章以获得完整的解释)

评论

2赞 RobG 1/7/2015
最好说它与“当前执行上下文”相关联。除了 ES6 (draft) 使用箭头函数更改它,这在外部执行上下文中得到解决。
49赞 arunjitsingh 6/27/2010 #4

“这个”是关于范围的。每个函数都有自己的作用域,由于 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 = value;
在它上面)。因此,为了克服这个(没有双关语)问题,开发人员可以:

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

试试看;你会开始喜欢这种编程模式

评论

6赞 Marcel Korpel 6/27/2010
“JS中的一切都是对象”是不正确的,JavaScript也有原始值,参见 bclary.com/2004/11/07/#a-4.3.2
6赞 arunjitsingh 7/5/2010
原始值本身似乎有一些方法,如 String#substring()、Number#toString() 等。因此,也许与那篇文章的命名法不同,它们确实表现得好像它们是对象一样(它们都是原型,即。String#substring() 实际上是:String.prototype.substring = function(){...})。如果我错了,请纠正我。
12赞 Bergi 12/9/2012
关键字与范围无关。此外,它在不是对象属性的函数中也有意义。this
1赞 RobG 1/7/2015
@arunjitsingh,关于这一点,有两种观点。我喜欢这样的说法:“一切都是一个对象,但为了方便起见,有些可以用基元表示”。;-)
9赞 Scott Marcus 3/16/2016
this并不是关于范围的全部。这完全是关于执行上下文的,这与范围不是一回事。JavaScript 是词法范围的(这意味着范围由代码的位置决定),但由包含它的函数的调用方式决定,而不是该函数的位置。this
11赞 Arman 4/23/2013 #5

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

简而言之,在 JavaScript 中,当前函数从谁(或从谁的执行上下文)指向对象,并且它始终是只读的,您无论如何都无法设置它(这样的尝试最终将以“赋值中左侧无效”消息结束。this

对于事件处理程序:内联事件处理程序(例如 )会覆盖之前和之前附加的任何其他处理程序,因此请小心,最好完全避免使用内联事件委派。 感谢 Zara Alaverdyan,她通过一场反对辩论启发了我这个例子清单:)<element onclick="foo">

  • el.onclick = foo; // in the foo - obj
  • el.onclick = function () {this.style.color = '#fff';} // obj
  • el.onclick = function() {doSomething();} // In the doSomething - Window
  • el.addEventListener('click',foo,false) // in the foo - obj
  • el.attachEvent('onclick, function () { // this }') // window, all the compliance to IE :)
  • <button onclick="this.style.color = '#fff';"> // obj
  • <button onclick="foo"> // In the foo - window, but you can <button onclick="foo(this)">
190赞 Mahesha999 7/8/2013 #6

与其他语言相比,该关键字在 JavaScript 中的行为不同。在面向对象语言中,关键字是指类的当前实例。在 JavaScript 中,的值由函数的调用上下文 (context.function()) 和调用位置决定。thisthisthis

1. 在全球上下文中使用时

当您在全局上下文中使用时,它被绑定到全局对象(在浏览器中)thiswindow

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

当你在全局上下文中使用一个函数时,它仍然绑定到全局对象,因为该函数实际上是一个全局上下文的方法。thisthis

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

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

function f()
{
    return this;
}

document.write(window.f()); //[object Window]

2.内部对象法使用时

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

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 object
document.write(obj1.f()); //[object Object]:obj1

3. 调用无上下文函数时

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

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, it 
     gets added to the function prototype, thus allowing all function 
     instances 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 behave 
      as above: all function instances have their own copy of the 
      variable 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 above 
      ways 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 />"); //v2
document.write(obj1.getVersion() + "<br />"); //v1

/********************************************************************* 
   4. `this` keyword refers to the immediate parent object. If you 
       nest the object through function prototype, then `this` inside 
       object 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` refers 
      to the object the method was called on, as if the method was on 
      the object.
*********************************************************************/
var ProtoObj = { fun: function () { return this.a } };
var obj3 = Object.create(ProtoObj); //creating an object setting ProtoObj
                                    //as its prototype
obj3.a = 999;                       //adding instance member to obj3
document.write(obj3.fun()+"<br />");//999
                                    //calling obj3.fun() makes 
                                    //ProtoObj.fun() to access obj3.a as 
                                    //if fun() is defined on obj3

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

当函数用作构造函数时(即使用 keyword 调用它时),函数体内部指向正在构造的新对象。newthis

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. 在 call()、apply() 和 bind() 函数中

  • 所有这些方法都定义在 上。Function.prototype
  • 这些方法允许编写一次函数并在不同的上下文中调用它。换句话说,它们允许指定在执行函数时将使用的值。它们还采用在调用原始函数时传递给原始函数的任何参数。this
  • fun.apply(obj1 [, argsArray])设置为 inside 的值,并调用传递元素 作为其参数。obj1thisfun()fun()argsArray
  • fun.call(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]])- 设置为 inside 的值,并调用作为其参数传递。obj1thisfun()fun()arg1, arg2, arg3, ...
  • fun.bind(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]])- 返回对函数的引用,其中 inside fun 绑定到 和 参数 绑定到指定的参数。funthisobj1funarg1, arg2, arg3,...
  • 到现在为止,和之间的区别一定已经很明显了。 允许指定参数以用作类似数组的对象,即具有数值属性和相应的非负整数属性的对象。而允许直接指定函数的参数。和 立即在指定的上下文中使用指定的参数调用函数。另一方面,只需返回绑定到指定值的函数和参数。我们可以通过将这个返回的函数分配给一个变量来捕获对这个返回函数的引用,然后我们可以随时调用它。applycallbindapplylengthcallapplycallbindthis
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 = 15
document.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 + 6
document.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 = 15
document.write(h() + "<br />");  //NaN
      //no parameter is passed to h()
      //thus inc2 inside add() is `undefined`
      //4 + 5 + undefined = NaN</code>

7. This 内部事件处理程序

  • 当您将函数直接分配给元素的事件处理程序时,直接在事件处理函数内部使用是指相应的元素。这种直接函数分配可以使用方法或通过传统的事件注册方法(如 )来完成。thisaddeventListeneronclick
  • 同样,当您直接在元素的 event 属性 (like ) 内部使用时,它指的是元素。this<button onclick="...this..." >
  • 然而,使用通过事件处理函数或事件属性内部调用的其它函数间接解析为全局对象。thiswindow
  • 当我们使用 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箭头函数

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

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

(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 
18赞 Seph 10/7/2013 #7

this在 JavaScript 中,始终指正在执行的函数的“所有者”。

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

所以如果我这样做了

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

element.onclick = someKindOfFunction;

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

<element onclick="someKindOfFunction()">

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

6赞 Nipuna 8/9/2014 #8

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

当执行 line1 时,JavaScript 会为函数调用建立执行上下文 (EC),设置为最后一个“.”之前的任何内容所引用的对象。因此,在最后一行中,您可以理解它是在全局上下文中执行的,即 .thisa()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

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

函数调用

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 

如果我们错过了关键字,则引用它能找到的最全局的上下文(newwhatIsThiswindow)

使用事件处理程序

如果事件处理程序是内联的,则引用全局对象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 添加事件处理程序时,指生成事件的 DOM 元素。this


77赞 user3459110 10/26/2014 #9

Javascript 的this

简单函数调用

请考虑以下函数:

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

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

在浏览器中运行时,的值将记录为 。这是因为它是 Web 浏览器作用域中的全局变量。thiswindowwindow

如果在 node.js 等环境中运行同一段代码,将引用应用中的全局变量。this

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

在以下情况下,我们将看到如何操作 的值。this

在对象上调用函数

有不同的方法可以做到这一点。如果你在 Javascript 中调用了 like 和 的原生方法,你应该已经知道在这种情况下,变量指的是你调用该函数的 (请注意,在 javascript 中,几乎所有东西都是 ,包括 s 和 s)。以以下代码为例。forEachslicethisObjectObjectArrayFunction

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

如果 an 包含一个属性,该属性包含 ,则该属性称为方法。调用此方法时,将始终将其变量设置为与其关联的变量。对于严格模式和非严格模式都是如此。ObjectFunctionthisObject

请注意,如果方法存储在另一个变量中(或者更确切地说,复制),则对 的引用将不再保留在新变量中。例如: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. 使用关键字调用函数将立即初始化 类型 。newObjectPerson
  2. this 的构造函数的构造函数设置为 。另外,请注意,这只会返回。ObjectPersontypeof awalObject
  3. 这个新将被分配 的原型。这意味着原型中的任何方法或属性都可用于 的所有实例,包括 。ObjectPerson.prototypePersonPersonawal
  4. 函数本身现在被调用; 是对新构造对象的引用。Personthisawal

很简单,对吧?

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

在 Functions: 和callapply

所以是的,由于 s 也是(实际上是 Javascript 中的第一类变量),即使是函数也有方法......好吧,功能本身。functionObjects

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

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

这是使用 .它基本上将函数中的第一个参数和集合作为对 的引用。传递给的所有其他参数将作为参数传递给函数。
所以上面的代码会登录到控制台。在任何函数中更改值的非常好的方法。
callthisfoothisArgcallfoo{myObj: "is cool"}, [1, 2, 3]this

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

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

请注意,和可以覆盖我们在第二个项目符号中讨论的 set by dot 方法调用的值。 简单到:)callapplythis

提出。。。。!bind

bind是 和 的兄弟。它也是所有函数从 Javascript 中的全局构造函数继承的方法。和 / 之间的区别在于,两者都将实际调用函数。另一方面,返回一个带有 和 预设的新函数。让我们举个例子来更好地理解这一点:callapplyFunctionbindcallapplycallapplybindthisArgarguments

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]`

看到这三者的区别了吗?这很微妙,但它们的用法不同。Like 和 ,也会通过点方法调用覆盖 set 的值。callapplybindthis

另请注意,这三个函数都不会对原始函数进行任何更改。 并将从新构造的函数中返回值,同时返回新构造的函数本身,准备被调用。callapplybind

额外的东西,复制这个

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

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

在上面的代码中,我们看到 的值随着嵌套范围的变化而变化,但我们希望从原始范围获得 的值。因此,我们“复制”并使用了副本而不是 .聪明,嗯?thisthisthisthatthis

指数:

  1. 默认持有什么?this
  2. 如果我们将函数作为具有 Object-dot 表示法的方法调用会怎样?
  3. 如果我们使用关键字呢?new
  4. 我们如何操纵 和 ?thiscallapply
  5. 用。bind
  6. 复制以解决嵌套范围问题。this
20赞 carlodurso 10/30/2014 #10

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

价值是如何确定的?this

我们使用它的方式类似于我们在英语等自然语言中使用代词的方式:“约翰跑得很快,因为他正试图赶上火车。相反,我们可以写成“......约翰正试图赶上火车”。

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 在对象调用定义它的函数之前,不会为其赋值。在全局作用域中,所有全局变量和函数都是在对象上定义的。因此,在全局函数中引用(并具有)全局对象的值。windowthiswindow

当 在未绑定到任何对象的全局函数和匿名函数中,当 的值为 。use strictthisundefined

当出现以下情况时,关键字最容易被误解:1)我们借用一个使用的方法,2)我们将一个使用的方法分配给一个变量,3)一个使用函数的函数作为回调函数传递,以及4)在闭包中使用 - 一个内部函数。(二)thisthisthisthisthis

table

什么把握着未来

ECMA 脚本 6 中定义,箭头函数采用来自 封闭(函数或全局)作用域。this

function foo() {
     // return an arrow function
     return (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. 这个和对象原型,凯尔·辛普森(Kyle Simpson)。© 2014 年 Getify 解决方案。
  2. javascriptissexy.com - http://goo.gl/pvl0GX
  3. 安格斯·克罗尔 - http://goo.gl/Z2RacU
11赞 zangw 11/29/2015 #11

这是一个很好的来源。thisJavaScript

摘要如下:

  • 全球这个

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

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

    在使用 repl 时,是顶部命名空间。您可以将其称为 .nodethisglobal

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

    在从脚本执行时,在全局范围内从空对象开始。它与nodethisglobal

    \\test.js
    console.log(this);  \\ {}
    console.log(this === global); \\ fasle
    
  • 函数这个

除了 DOM 事件处理程序或提供 a 的情况(见下文),无论是在节点中还是在浏览器中使用未调用引用的函数中,全局范围......thisArgthisnew

<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;thisundefined

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

如果使用 will be a new context 调用函数,则它不会引用全局 .newthisthis

<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>
  • 原型这个

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

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

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

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

obj.logFoo(); //logs "bar"
  • DOM 事件 this

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

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();
  • HTML 这个

在 HTML 属性中,您可以在其中放置 JavaScript,是对元素的引用。this

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

您可以使用访问 .evalthis

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

var thing = new Thing();
thing.logFoo();
  • 有了这个

您可以使用添加到当前作用域以读取和写入值,而无需显式引用。withthisthisthis

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在很多地方都引用了DOM元素。this

<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>
1赞 PRADEEP SINGH Chundawat #12

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 different
var txt1='';
 txt1+=$('#tbleName tbody tr').eq(0).text();
alert(txt1)
});
</script>

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

7赞 James-Jesse Drinkard 7/13/2016 #13

这是我见过的最好的解释:清晰地理解 JavaScript

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

有四种情况可能会造成混淆:

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

他提供了代码示例、解释和解决方案,我认为这非常有帮助。

9赞 Dmitri Pavlutin 11/8/2016 #14

可能最详细和最全面的文章如下:this

JavaScript 中“this”关键字的温和解释

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


当识别有困难时,不要问自己:this

这是从哪里来的?

一定要问问自己:

函数是如何调用的?

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

定义箭头函数的地方有什么值?this

这种心态在处理时是正确的,可以避免头痛。this

评论

0赞 Magnus Lind Oxlund 6/2/2019
除了链接到你的博客之外,也许你可以更深入地研究提出这些问题如何帮助人们理解关键词?this
9赞 Arup Hore 1/31/2017 #15

关于“this”关键字在 JavaScript 中的解释方式存在很多混淆。希望本文能一劳永逸地让所有这些人安息。还有更多。请仔细阅读整篇文章。请注意,这篇文章很长。

无论在何种上下文中使用,“this”始终引用 Javascript 中的“当前对象”。但是,“当前对象”是什么因上下文而异。上下文可能正好是以下 6 项中的 1 项:

  1. 全局(即所有函数之外)
  2. 内部直接“非绑定函数”调用(即未通过调用 functionName.bind 绑定的函数)
  3. 通过 functionName.callfunctionName.apply 进行间接“非绑定函数”调用
  4. 在“绑定函数”调用(即通过调用 functionName.bind 绑定的函数)中)
  5. 虽然通过“new”创建对象
  6. 内联 DOM 事件处理程序内部

下面逐一介绍每个上下文:

  1. 全局上下文(即所有功能之外):

    在所有功能之外(即在全球上下文中),“当前 object“(因此”this“的值)始终是浏览器的”window“对象。

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

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

    function UserDefinedFunction(){
        alert(this)
        }
    

    它成为 Window 对象的属性,就像您定义了一样 它作为

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

    在“非严格模式”下,直接通过“UserDefinedFunction()”调用/调用此函数将自动调用/调用 它作为“窗口。UserDefinedFunction()“,使”window“成为”UserDefinedFunction“中的”当前对象“(以及”this“的值)。在“非严格模式”下调用此函数将导致以下结果

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

    在“严格模式”下,直接通过“UserDefinedFunction()”调用/调用函数将“不”自动将其调用/调用为“窗口”。UserDefinedFunction()”。因此,“当前 object“(以及”this“的值)在”UserDefinedFunction“中应未定义。在“严格模式”下调用此函数将导致以下结果

    UserDefinedFunction() // displays undefined
    

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

    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,undefined
    o2.f() // Shall display undefined,undefined,3,4
    

    在上面的例子中,我们看到当 “UserDefinedFunction” 通过 O1 调用,“this”O1 的值,并且 显示其属性“a”和“b”的值。价值 的“C”和“D”显示为未定义,就像 O1 一样 不定义这些属性

    同样,当通过 o2 调用“UserDefinedFunction”时,“this”的值为 o2,并显示其属性“c”和“d”的值。a”和“b”的值显示为未定义,因为 o2定义这些属性。

  3. 通过 functionName.callfunctionName.apply 进行间接“非绑定函数”调用:

    当通过 functionName.callfunctionName.apply 调用“非绑定函数”时,“当前对象”(以及“this”的值)被设置为传递给 call/apply“this”参数(第一个参数)的值。下面的代码演示了相同的内容。

    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,undefined
    UserDefinedFunction.apply(o1) // Shall display 1,2,undefined,undefined
    
    UserDefinedFunction.call(o2) // Shall display undefined,undefined,3,4
    UserDefinedFunction.apply(o2) // Shall display undefined,undefined,3,4
    
    o1.f.call(o2) // Shall display undefined,undefined,3,4
    o1.f.apply(o2) // Shall display undefined,undefined,3,4
    
    o2.f.call(o1) // Shall display 1,2,undefined,undefined
    o2.f.apply(o1) // Shall display 1,2,undefined,undefined
    

    上面的代码清楚地表明,任何“NON”的“this”值 绑定函数“可以通过调用/应用进行更改。此外,如果未显式传递“this”参数来调用/应用,则“当前对象”(以及“this”的值)在非严格模式下设置为“window”,在严格模式下设置为“undefined”。

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

    绑定函数是其“this”值为 固定。以下代码演示了“this”在 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 o1
    bound1() // Shall display 1,2,undefined,undefined
    
    var bound2=UserDefinedFunction.bind(o2); // permanantly fixes "this" value of function "bound2" to Object o2
    bound2() // Shall display undefined,undefined,3,4
    
    var bound3=o1.f.bind(o2); // permanantly fixes "this" value of function "bound3" to Object o2
    bound3() // Shall display undefined,undefined,3,4
    
    var bound4=o2.f.bind(o1); // permanantly fixes "this" value of function "bound4" to Object o1
    bound4() // Shall display 1,2,undefined,undefined
    
    o1.bf=UserDefinedFunction.bind(o2) // permanantly fixes "this" value of function "o1.bf" to Object o2
    o1.bf() // Shall display undefined,undefined,3,4
    
    o2.bf=UserDefinedFunction.bind(o1) // permanantly fixes "this" value of function "o2.bf" to Object o1
    o2.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 function
    o2.bf.apply(o2) // Shall still display 1,2,undefined,undefined."apply" cannot alter the value of "this" for bound function
    

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

  5. 而通过“new”创建对象

    在构造函数中,“当前对象”(以及“this”的值)引用当前正在创建的对象 通过“new”,而不考虑函数的绑定状态。然而 如果构造函数是一个绑定函数,则应使用 为绑定函数设置的预定义参数集。

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

    请查看以下 HTML 代码段

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

    上面示例中的“this”是指“button”元素,而 “div”元素。

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

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

以下总结了整篇文章

  1. 在全局上下文中,“this”始终是指“window”对象

  2. 每当调用函数时,都会在 对象(“当前对象”)。如果未显式提供当前对象当前对象是 NON Strict 中的“窗口对象” 默认情况下,Mode 和 Strict Mode 中的“undefined”。

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

  4. 非绑定函数中“this”的值可以通过调用应用函数的方法覆盖。

  5. “this”的值对于 Bound 函数是固定的,不能 被函数的 callapply 方法覆盖。

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

  7. 构造函数中“this”的值是 创建并初始化

  8. 内联 DOM 事件处理程序中“this”的值是引用 设置为为其提供事件处理程序的元素。

6赞 Trombe 7/3/2017 #16

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

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

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

6赞 mrmaclean89 9/17/2017 #17

在伪经典术语中,许多讲座教授“this”关键字的方式是由类或对象构造函数实例化的对象。每次从类构造一个新对象时,想象一下在后台创建并返回“this”对象的本地实例。我记得它是这样教的:

function Car(make, model, year) {
var this = {}; // under the hood, so to speak
this.make = make;
this.model = model;
this.year = year;
return this; // under the hood
}

var mycar = new Car('Eagle', 'Talon TSi', 1993);
// ========= under the hood
var this = {};
this.make = 'Eagle';
this.model = 'Talon TSi';
this.year = 1993;
return this;
3赞 unclexo 7/25/2018 #18

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

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

console.log(this)

Client/Browser 关键字中是一个全局对象,它是thiswindow

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

Server/Node/Javascript 运行时关键字中也是一个全局对象,它是thismodule.exports

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

请记住,这只是对exportsmodule.exports

1赞 Willem van der Veen 8/22/2018 #19

总结 Javascript:this

  • 的值取决于函数的调用方式,而不是函数的创建位置!this
  • 通常,的值由点左边的 Object 确定。( 在全球空间)thiswindow
  • 在事件侦听器中,值 引用调用事件的 DOM 元素。this
  • 当使用关键字调用 in 函数时,value of 引用新创建的对象newthis
  • 您可以使用以下函数操作 的值: , ,thiscallapplybind

例:

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>

示例构造函数:

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.

3赞 gman 1/29/2019 #20

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

查看 JavaScript 的一种方法是查看只有 1 种方法可以调用函数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);

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

'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 bar
bar.fn2();  // this is bar
bar.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); }

另一个皱纹是原型链。当您使用 JavaScript 时,首先查看 for 属性直接引用的对象。如果在对象上找不到,那么 JavaScript 将在对象的原型中查找 .a.babbb

定义对象原型的方法有很多种,2019 年最常见的是关键字。为了目的,虽然没关系。重要的是,当它在对象中寻找属性时,如果它在对象上找到属性,或者在它的原型链中找到属性,如果最终成为一个函数,那么与上述相同的规则适用。将使用该方法调用函数引用,并将其作为 objectForThis 传递,如此答案的顶部所示。classthisabbbbcalla

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

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

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

const obj = {
  bar,
};

obj.bar();  

在翻译后使用 ,变成 。当我们输入我们调用的函数时,但我们显式地为 objectForThis 传入了另一个对象,所以当我们到达 foo 时,就是那个内部对象。callobj.bar()const temp = obj.bar; temp.call(obj);barfoothis

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

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'});

箭头函数,运算符是绑定的句法糖=>

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

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

就像 一样,创建了一个新的不可见函数,该函数使用绑定值调用给定函数,但与要绑定的对象不同的是隐式的。这是使用运算符时发生的任何事情。bindobjectForThisbindthis=>

所以,就像上面的规则一样

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()转换为 which 表示里面的箭头运算符将绑定到一个新的不可见函数,并返回分配给 的新不可见函数。 将一如既往地工作,或者调用创建的新不可见函数。该不可见函数忽略传入其中的内容,并将 objectForThis' 作为 objectForThis' 传递给箭头函数。const temp = obj.foo; temp.call(obj);fooobjbb()b.call(window)b.call(undefined)foothisobj

上面的代码转换为

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);

1 是另一个类似于applycall

functionName.apply(objectForThis, arrayOfArgs);

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

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

评论

0赞 John Winston 3/3/2021
伙计,你的解释非常有效。完全清除了我的困惑。
0赞 Rafiq 5/20/2020 #21

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

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

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

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

现在让我们看一些例子:

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

在浏览器输出中运行 abobve 代码将:enter image description here

根据您在窗口对象上下文中的输出,还可以看到窗口原型引用了该对象。

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

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

输出:

enter image description here输出是相同的,因为我们在全局范围内记录了“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>

输出:enter image description here

所以从上面的例子中,我们发现 '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 element 
            console.log(this);
            printMe();
        })
    </script>
</body>

输出: 有道理吧?(阅读评论)enter image description here

如果你对上面的例子有困难,让我们尝试使用我们自己的回调;

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

输出:enter image description here

现在让我们了解 Scope、Self、IIFE 和 THIS 的行为方式

       var color = 'red'; // property of window
       var obj = {
           color:'blue', // property of window
           printColor: function(){ // property of obj, attached with obj
               var 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 context
               return nestedFunc;
           }
       };

       obj.printColor()(); // returned function executed on window context
   </script> 

输出非常棒,对吧?enter image description here

3赞 Arham Chowdhry 11/30/2021 #22

JavaScript 中的“this” 这是 Execution Context 的属性之一。enter image description here

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

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

1. 在方法内部。

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

enter image description here在上面的例子中,方法 “fullName()” 由对象 “person” 调用,因此 “ fullName()” 方法中的 this 值将等于 “person” 对象。

2. 函数内部。

i) 函数声明/表达式

在松散模式下,此 === 窗口(对象)enter image description here

在严格模式下,这 === 未定义enter image description here

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

ii) 箭头功能:

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

enter image description here

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

enter image description here

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

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

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

它们都可用于调用一个对象方法,并将另一个对象作为参数。

enter image description here

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

您可以在此处了解有关 call、apply 和 bind 方法的更多信息。

评论

0赞 Sebastian Simon 12/1/2021
这里接受的答案是正确的、最新的和完整的。代码示例不应采用屏幕截图的形式,而应以可复制的代码块的形式出现(也适用于您之前的答案)。“每次执行函数时都会创建此属性,而不是在此之前创建”是不正确的:它缺少类和全局执行上下文。“取决于它的使用方式”是相当模糊的。“函数的所有者”并不是 JS 中真正的语言结构。对象不能“调用”方法。可以在对象(或“关闭”)调用方法。“正常模式”不如严格模式正常......
0赞 Sebastian Simon 12/1/2021
一般来说,应该提到而不是 .“箭头函数没有自己的此属性”是含糊不清的。 不是对象(ES 语言值)的属性,而是环境记录(ES 规范值)上的内部槽。箭头函数不绑定“如果它们是在全局级别定义的”,则严格与宽松模式的相同规则适用。事件侦听器实际上并不特别; 调用绑定到接收事件的属性的侦听器函数。globalThiswindowthisthisaddEventListenerthiscurrentTarget
0赞 Sebastian Simon 12/1/2021
有几个 API 绑定了一些值,但您可以创建自己的 API 来执行此操作。“在上面的例子中”?示例如下this
0赞 Arham Chowdhry 12/1/2021
@SebastianSimon我真的很尊重你花时间阅读我的答案。我真的很感谢你的回答,这是最新的。但我认为它对初学者没有多大用处,因为它太长了,而且,如果我想要一个详细的概念,我会去官方的 MDN 文档。我的回答尽可能简短明了。提供的屏幕截图仅供快速查看,将来我也会尝试放置代码。