8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

'this' 关键字如何工作?何时使用?

ekse 2月前

231 0

我正在寻找一个清晰的解释,说明 \'this\' 关键字的作用以及如何正确使用它。它似乎表现得很奇怪,我不完全明白为什么。它是如何工作的,

我正在寻找对“this”关键字的作用以及如何正确使用它的清晰解释。

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

如何 this 工作以及何时使用?

帖子版权声明 1、本帖标题:'this' 关键字如何工作?何时使用?
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由ekse在本站《object》版块原创发布, 转载请注明出处!
最新回复 (0)
  • this 是 JavaScript 中的一个关键字,是执行上下文的一个属性。它主要用于函数和构造函数。它的规则 this 非常简单(如果你遵循最佳实践)。

    规范中 this 的技术描述

    ECMAScript 标准 通过抽象操作(缩写为 this 这一点 AO ) ResolveThisBinding

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

    1. envRec 成为 GetThisEnvironment ()。
    2. 返回? envRec.GetThisBinding ()。

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

    AO 查找当前 GetThisEnvironment 正在运行的执行上下文 的 LexicalEnvironment,并查找最近的具有 this this true 。此过程以三种环境记录类型之一结束。

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

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

    您可以将代码示例放入 AST 资源管理器 ,以便跟踪规范详细信息。

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

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

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

    当处于脚本的初始全局执行上下文中时,评估 this 会导致 GetThisBinding 采取以下步骤:

    的 GetThisBinding 具体方法 envRec […] [执行以下操作]:

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

    全局环境记录的 [[GlobalThisValue]] 属性始终设置为主机定义的 全局对象 ,该对象可通过 globalThis window 在 Web 上、 global 在 Node.js 上; 在 MDN 上查看文档 的步骤 InitializeHostDefinedRealm 了解 [[GlobalThisValue]] 属性是如何产生的。

    模块 中的全局执行上下文

    ECMAScript 2015 中引入了模块。

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

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

    模块环境记录的 GetThisBinding 具体方法 […] [执行以下操作]:

    1. 返回 undefined .

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

    3. 输入 eval 代码

    有两种类型的 eval 调用: 直接调用 间接调用 。这种区别从 ECMAScript 第 5 版开始就存在了。

    • 直接 eval 调用通常看起来像 eval( ... ); (eval)( ... ); (或 ((eval))( ... ); 等)。1只有当调用表达式符合狭窄模式时, 它才是 直接的。2
    • 间接 eval 以任何其他方式 eval 调用函数引用 eval?.( 等。给定,它。另外,给定………………………… ) , ( , eval)( ) , window.eval( ) , eval.call( , ) 也可能为 const aliasEval1 = eval; window.aliasEval2 = eval; …… aliasEval1( ) , aliasEval2( ) const originalEval = eval; window.eval = (x) => originalEval(x); 调用 eval( ) 也是间接的。

    请参阅 chuckj’s answer to “(1, eval)('this') vs eval('this') in JavaScript?” 以及 Dmitry Soshnikov 的 ECMA-262-5 详细信息 – 第 2 章:严格模式 ( 已存档 ),了解何时可以使用间接 eval() 调用。

    PerformEval 执行 eval 代码。它创建一个新的 声明性环境记录 作为其词法环境, GetThisEnvironment 获取 this 值。

    然后,如果 this 出现在 eval 则调用 GetThisEnvironment 找到的环境记录的 GetThisBinding 方法

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

    这意味着:

    • 在直接求值中, this 值不会改变;它是从调用的词法作用域中获取的 eval .
    • 在间接求值中, this 值是全局对象 ( globalThis )。

    What about new Function ? —— new Function 与 类似 eval ,但它不会立即调用代码;它会创建一个函数。 this 绑定在这里不适用于任何地方,除非调用函数,这将正常工作,如下一小节所述。

    4. 输入 功能 代码

    调用 函数 时输入函数代码

    调用函数的语法有四类。

    实际函数调用发生在 调用 AO 中,它使用 thisValue ;此参数在与调用相关的长链中传递。Call Call 函数的 [[Call]] PrepareForOrdinaryCall 创建 一个新的 函数环境记录

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

    另外,函数环境记录中有一个[[ThisValue]]字段:

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

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

    [[Call]] 还调用 OrdinaryCallBindThis ,其中适当的 thisArgument 根据以下内容确定:

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

    一旦确定,最后调用 新创建的函数 Environment Record 的 BindThisValue thisArgument .

    最后,这个字段是 函数环境记录的 GetThisBinding AO 获取其值的 this

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

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

    这个 究竟如何 确定取决于许多因素;这只是一个总体概述。有了这些技术背景,让我们来研究一下所有具体的例子。

    箭头函数

    箭头函数 “lexical” in OrdinaryFunctionCreate .

    OrdinaryCallBindThis ,它采用函数 F

    1. thisMode F .[[ThisMode]].
    2. 如果 th是Mode is 词汇的 ,则返回NormalCompletion( undefined )。[…]

    的其余算法 this 。箭头函数不绑定其自己的 this 值。

    那么, this 箭头函数里面是什么呢?回顾 ResolveThisBinding GetThisEnvironment HasThisBinding method explicitly returns false .

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

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

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

    这只是意味着, in arrow function bodies, this comes from the lexical scope of the arrow function ,或者换句话说(来自 箭头函数与函数声明/表达式:它们是否等效/可交换? ):

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

    函数 属性

    在普通函数( function , 方法 )中, this 由函数的调用方式 决定 .

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

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

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

    或者:

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

    在以下任何函数调用中, this 里面的值 func 都将为 refObj .1

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

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

    而且, refObj.func refObj 扮演三个角色:

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

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

    可选链式调用和标记模板示例的工作原理非常相似:基本上,引用是 之前 ?.() 、 之前 `` 或 之前的 () .

    EvaluateCall 使用 IsPropertyReference 来确定它是否是对象的属性。它试图获取引用的 [[Base]] 属性(例如 refObj ,当应用于 时 refObj.func ;或 foo.bar 当应用于 时 foo.bar.baz )。如果它被写为属性,则 GetThisValue 将获取此 [[Base]] 属性并将其用作 this 值。

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

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

    不使用基引用、使用严格模式以及 with

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

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

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

    注意函数 j :按照规范,您会注意到 j 只能返回函数对象(值)本身,而不能返回引用记录。因此基本引用 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.
    

    Evaluate未定义 使用 thisValue 传递给 调用 Call 中有所不同 OrdinaryCallBindThis F :函数对象; thisArgument thisValue Call Call ):

    1. thisMode F .[[ThisMode]].

    […]

    1. 如果 th是Mode is strict ,则让 thisValue thisArgument .
    2. 别的,
      1. If thisArgument is undefined or null , then
        1. Let globalEnv be calleeRealm .[[GlobalEnv]].
        2. […]
        3. Let thisValue be globalEnv .[[GlobalThisValue]].
      2. Else,
        1. Let thisValue be ! ToObject (thisArgument).
        2. NOTE: ToObject produces wrapper objects […].

    […]

    ,步骤 5 在严格模式下 this 的实际值设置 为提供的 thisArgument undefined 。在“松散模式”下,未定义或 null 的 thisArgument 将导致 this 全局 this 值。

    如果 IsPropertyReference 返回 false ,则 EvaluateCall 采取以下步骤:

    1. refEnv 成为 ref .[[Base]].
    2. 断言: refEnv 是一个环境记录。
    3. thisValue 成为 refEnv.WithBaseObject ()。

    这就是未定义的 thisValue 可能来自的地方: refEnv . WithBaseObject () 始终 未定义, , 除非 with with 。在这种情况下, thisValue 将是绑定对象。

    还有 Symbol.unscopables MDN 上的文档 )来控制 with 绑定行为。

    总结一下,到目前为止:

    function f1(){
      console.log(this);
    }
    
    function f2(){
      console.log(this);
    }
    
    function f3(){
      console.log(this);
    }
    
    const o = {
        f1,
        f2,
        [Symbol.unscopables]: {
          f2: true
        }
      };
    
    f1(); // Logs `globalThis`.
    
    with(o){
      f1(); // Logs `o`.
      f2(); // `f2` is unscopable, so this logs `globalThis`.
      f3(); // `f3` is not on `o`, so this logs `globalThis`.
    }
    

    和:

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

    请注意,在评估时, this , it doesn’t matter where a normal function is defined .

    .call , .apply , .bind , thisArg 和原语

    的步骤 5 OrdinaryCallBindThis 与步骤 6.2(规范中的 6.b)相结合的另一个结果是,原始 this 在“草率”模式下 才会被强制转换为对象

    为了检查这一点,让我们介绍 三个方法 this this 4

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

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

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

    设置自定义 this 值的示例:

    function f(){
      console.log(this);
    }
    
    const myObj = {},
      g = f.bind(myObj),
      h = (m) => m();
    
    // All of these log `myObj`.
    g();
    f.bind(myObj)();
    f.call(myObj);
    h(g);
    

    对于对象来说,在严格和非严格模式下这都是一样的。

    现在,尝试提供一个原始值:

    function f(){
      console.log(this);
    }
    
    const myString = "s",
      g = f.bind(myString);
    
    g();              // Logs `String { "s" }`.
    f.call(myString); // Logs `String { "s" }`.
    

    Object("s") 时获得的对象类型相同 new String("s") 。在严格模式下,您 可以 使用原语:

    "use strict";
    
    function f(){
      console.log(this);
    }
    
    const myString = "s",
      g = f.bind(myString);
    
    g();              // Logs `"s"`.
    f.call(myString); // Logs `"s"`.
    

    库利用这些方法,例如 jQuery 将设置 this 为此处选择的 DOM 元素:

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

    构造函数、 new

    当使用运算符将​​函数作为构造函数调用时 new EvaluateNew 会调用 Construct ,后者会调用 [[Construct]] 方法 。如果该函数是基构造函数(即不是 class extends )… { } ,它会将 thisArgument 为从构造函数的原型创建的新对象。 this 构造函数中设置的属性最终将出现在生成的实例对象上。 this 除非您明确返回自己的非原始值,否则将隐式返回。

    class ECMAScript 2015 中引入的一种创建构造函数的新方法。

    function Old(a){
      this.p = a;
    }
    
    const o = new Old(1);
    
    console.log(o);  // Logs `Old { p: 1 }`.
    
    class New{
      constructor(a){
        this.p = a;
      }
    }
    
    const n = new New(1);
    
    console.log(n); // Logs `New { p: 1 }`.
    

    类定义隐式处于 严格模式

    class A{
      m1(){
        return this;
      }
      m2(){
        const m1 = this.m1;
        
        console.log(m1());
      }
    }
    
    new A().m2(); // Logs `undefined`.
    

    极好的

    ,使用 行为的例外 new class extends { } 。派生类不会在调用时立即设置其 this 值;它们仅在通过一系列 super 调用到达基类后才这样做(在没有 own 的情况下隐式发生 constructor 不允许 this 在调用之前 super 使用

    使用 super 调用的词法范围(函数环境记录)的 this 调用 super 构造函数 对调用有一条特殊规则 super 。它使用 BindThisValue 来设置 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 a 时 将执行 ClassDefinitionEvaluation 正在运行的执行上下文 。对于每个 ClassElement

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

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

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

    请注意,在方法和 getter / 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: 存档o.f)() 等同于 o.f() ; (f)() 等同于 f() 2ality 文章 ( ( 中有解释 。具体请参见 how a ParenthesizedExpression is evaluated .

    2:它必须是 MemberExpression ,不能是属性,必须具有恰好为 \'eval\' ,并且必须是 %eval% 内在对象。

    3:每当规范说 “让 ref X 的结果 X 就是某个表达式,您需要找到其评估步骤。例如,评估 MemberExpression or CallExpression 这些算法 之一的结果 。其中一些算法会产生 引用记录 .

    4:还有其他几种本机和主机方法允许提供 this 值,特别是 Array.prototype.map , Array.prototype.forEach 等,它们接受 thisArg 作为其第二个参数。任何人都可以创建自己的方法来进行修改 this 例如 (func, thisArg) => func.bind(thisArg) , (func, thisArg) => func.call(thisArg) 等。与往常一样, MDN 提供了出色的文档。


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

    对于每个代码片段,回答以下问题: “What is the value of this at the marked line? Why?” .

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

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

      p94

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

      p95

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

      p96

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

      p97

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

      p98

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

      p99

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

      p100

  • 另一种常见情况:EventHandlers 被调用时将 this 设置为 Event 的 currentTarget。未来可以包含以下三个提议:绑定运算符 ::、显式 this、this 参数反射。DOM 0 事件属性(如 onclick)也值得注意:JS 代码被隐式包装在一个范围为 document 的属性和一个范围为被点击元素的属性中,从而造成混淆;这是具有该属性的元素。

  • @LRDPRDX 好吧,这个答案包含了所有关于这个的细节。但是没有人真的需要在全局范围内使用这个,with 已被弃用,不鼓励使用 eval,应该在任何地方使用严格模式,等等。剩下的就是,如果 method 是一个函数或方法,obj.method() 会以 obj 作为 this 来调用 method;func() 会调用 func 而不使用任何 this;.bind、.call 和 .apply 可用于明确绑定 this;箭头函数没有 this 绑定。类:在静态事物中,this 指的是类本身,在非静态事物中指的是正在创建的实例。就是这样。

  • 关键字 this 在 JavaScript 中的行为有所不同。在面向对象语言中, this 关键字指的是类的当前实例。在 JavaScript 中,的值 this 由函数的调用上下文( context.function() )及其调用位置决定。

    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 将绑定到\'immediate\'封闭对象。

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

    上面我将“immediate”一词放在了双引号中。这是为了说明,如果你将对象嵌套在另一个对象中,则 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. 调用无上下文函数时

    当您使用在 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, 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. 在构造函数中使用时

    当函数作为构造函数使用时(即用 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. call()、apply() 和 bind() 函数内部

    • 所有这些方法都定义在 Function.prototype .
    • 这些方法允许编写一次函数并在不同的上下文中调用它。换句话说,它们允许指定 this 在执行函数时将使用的值。它们还会在调用时接受要传递给原始函数的任何参数。
    • fun.apply(obj1 [, argsArray]) 将其设置 obj1 内部 this 的值 fun() 并调用 fun() 传递的元素作为 argsArray 其参数。
    • fun.call(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]]) - 设置 obj1 内部 this 的值 fun() 并调用 fun() 传递 arg1, arg2, arg3, ... 的参数。
    • fun.bind(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]]) 绑定到内部 fun this 引用 obj1 ,并将参数 fun 绑定到指定的参数 arg1, arg2, arg3,... .
    • 现在 apply , call bind 一定已经很明显了。 apply 允许将函数的参数指定为类似数组的对象,即具有数字 length 属性和相应的非负整数属性的对象。而 call 允许直接指定函数的参数。 apply call 在指定的上下文中使用指定的参数立即调用该函数。另一方面, 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 = 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

    • 当您将函数直接分配给元素的事件处理程序时,事件处理函数内部的直接使用 this 方法或通过传统的事件注册方法 addeventListener 完成, onclick .
    • 类似地,当您 this 直接在元素的事件属性(如 <button onclick="...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 箭头函数中的 this

    在箭头函数中, 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 
    
  • Dog 2月前 0 只看Ta
    引用 6

    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 中调用过本机方法(如 forEach 和 ) slice ,那么您应该已经知道, this 在这种情况下,变量指的是 Object 您调用该函数的 (请注意,在 Javascript 中,几乎所有东西都是 Object ,包括 Array s 和 Function s)。以以下代码为例。

    var myObj = {key: "Obj"};
    myObj.logThis = function () {
        // I am a method
        console.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 将立即初始化一个 Object 类型 Person .
    2. 此构造函数的 Object 构造函数设置为 Person 。另请注意, typeof awal 只会返回 Object
    3. 这个新的 Object 将被分配到 的原型 Person.prototype 。这意味着 Person 原型中的任何方法或属性都将可供 的所有实例使用 Person ,包括 。 awal .
    4. 函数 Person 本身现在被调用; this 作为对新构造对象的引用 awal .

    很简单,是吧?

    请注意,官方 ECMAScript 规范中没有任何地方指出此类函数是实际 constructor 函数。它们只是普通函数, new 可用于任何函数。我们只是这样使用它们,因此我们仅这样称呼它们。

    在函数中调用函数: call apply

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

    所有函数都从全局的 继承 Function ,它的众多方法中的两个是 call apply 调用它们的函数中 this 操纵 的值

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

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

    apply 与 accept 几乎相同 call ,它只接受两个参数: thisArg 以及一个包含要传递给函数的参数的数组。因此,上述 call 调用可以转换为 apply 如下形式:

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

    请注意, call apply 我们在第二条讨论的通过点方法调用设置 this 的值

    呈现.... bind

    bind <之间的区别code>的兄弟 call / apply 。它也是 JavaScript 中所有函数从全局构造函数继承的方法 Function and bind and call / apply 在于, call and apply 都会实际调用该函数。 bind 而 则返回一个带有 thisArg and arguments 预设的新函数。让我们举一个例子来更好地理解这一点:

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

    发现三者的区别了吗?虽然很微妙,但用法却不同。就像 和 一样,它们 call 也会 apply , bind 通过点方法调用 this 覆盖 set 的值

    还要注意,这三个函数都不会对原始函数进行任何更改。会 call and apply 返回新构造的函数的值,而 while bind 会返回新构造的函数本身,随时可以调用。

    额外内容,复制此

    有时,你不喜欢 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"
            });
        }
      };
    

    在上面的代码中,我们看到 的值 this 原始范围中 this 的 的值 this that 并使用副本而不是 this 。很聪明,不是吗?

    指数:

    1. 保存了什么 this
    2. 如果我们用对象点符号将该函数作为方法调用会怎么样?
    3. 如果我们使用 new 关键字会怎么样?
    4. 我们 this 怎样操纵 call apply
    5. 使用 bind .
    6. 复制 this 来解决嵌套范围问题。
  • sarh 2月前 0 只看Ta
    引用 7

    \'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\' 并不包含您期望的 \'value\'(来自

    this.value = value;
    above it). So, to get over this (no pun intended) problem, a developer could:
    (function(value) {
        var self = this;            // small change
        self.value = value;
        $('.some-elements').each(function(elt){
            elt.innerHTML = self.value;        // phew!! == 2 
        });
    })(2);
    

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

  • “JS 中的一切都是对象”是不正确的,JavaScript 也有原始值,请参阅 bclary.com/2004/11/07/#a-4.3.2

  • 原始值似乎本身就有一些方法,例如 String#substring()、Number#toString() 等。因此,也许与那篇文章的命名法不同,它们的行为实际上就像对象一样(它们都是原型化的,即 String#substring() 实际上是:String.prototype.substring = function(){...})。如果我错了,请纠正我。

  • @arunjitsingh—对此有两种观点。我喜欢这样一种观点,“一切都是对象,但为了方便起见,有些可以用原语来表示”。;-)

  • 这并非全部与范围有关。这完全与执行上下文有关,这与范围不同。JavaScript 是词汇范围(意味着范围由代码的位置决定),但这是由包含它的函数的调用方式决定的 - 而不是该函数的位置。

  • 引用 12

    由于这个话题已经被提及,我为刚接触这个主题的读者整理了几点要点。

    的值是如何 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 直到对象调用定义它的函数时,它 才会被赋值 window 对象上定义的。因此, this 全局函数引用(并具有)全局对象 window

    use strict , this 在全局函数和未绑定到任何对象的匿名函数中持有以下值 undefined .

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

    table

    未来由什么决定

    中定义 ECMA Script 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)


    参考:

    1. this & Object Prototypes ,作者:Kyle Simpson。© 2014 Getify Solutions。
    2. javascriptissexy.com - http://goo.gl/pvl0GX
    3. 安格斯克罗尔 - http://goo.gl/Z2RacU
  • this 执行的 函数的“所有者” .

    如果没有定义明确的所有者,则引用最顶层的所有者,即窗口对象。

    所以如果我这么做

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

    element.onclick = someKindOfFunction;

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

    <element onclick="someKindOfFunction()">

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

  • JavaScript 中的 每个 执行上下文 this 参数,其设置方式如下:

    1. 如何调用函数(包括作为对象方法、使用 call apply 、使用 new )
    2. 使用 bind
    3. 箭头函数的词汇表(它们采用 其外部执行上下文的 this
    4. 代码是否处于严格模式或非严格模式
    5. 代码是否使用 eval

    使用 func.call , func.apply func.bind .

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

    jQuery 使用 jQuery.proxy 使得这一改变变得简单。

  • 更正确的说法是每个函数调用都有一个作用域。换句话说,JavaScript 中令人困惑的是,它不是函数本身的固有属性,而是函数调用方式的产物。

  • 引用 16

    @pointy 谢谢。在 js 中,最让人困惑的一点是,在之前使用的所有语言(c#、c++)中,this 都无法被操纵,并且始终指向对象实例,而在 js 中,它依赖于此,并且可以在使用 func.call、func.bind 等调用函数时进行更改。– Sushil

  • this 不引用函数的作用域。this 将引用特定对象(或可能未定义),正如您所说的,可以使用 .call() 或 .apply() 进行更改。函数的作用域(本质上,简化后)是它可以访问哪些变量,这完全取决于函数声明的位置,并且无法更改。

  • @Pointy: \'说每个函数调用都有一个作用域更正确一些。\' 说函数(现在还有块)有作用域,函数调用有上下文更正确。作用域定义了该作用域内的代码可以使用的标识符。上下文定义了这些标识符绑定到什么。

  • \'无论该范围是什么,都由 \'this\' 引用。\' 不,this 和范围在 ES5 及之前版本中彼此没有任何关系(例如,当撰写此答案时)。在 ES2015(又名 ES6)中,this 和范围与箭头函数的关系非常小(箭头函数中的 this 继承自其封闭范围),但 this 永远不会引用范围。

  • 引用 20

    丹尼尔,解释得太棒了!关于这一点,我简单说几句,并列出了 this 事件处理程序中执行上下文指针的良好列表。

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

    对于事件处理程序: 内联事件处理程序(例如 <element onclick="foo"> )会覆盖之前附加的任何其他处理程序,因此请小心,最好完全远离内联事件委托。感谢 Zara Alaverdyan 通过不同意见辩论启发我编写此示例列表 :)

    • 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)">
返回
作者最近主题: