【问题标题】:Javascript "this" pointer within nested function嵌套函数中的Javascript“this”指针
【发布时间】:2012-03-27 11:50:32
【问题描述】:

我有一个关于如何在嵌套函数场景中处理“this”指针的问题。

假设我将以下示例代码插入网页。调用嵌套函数“doSomeEffects()”时出现错误。我检查了 Firebug,它表明当我在那个嵌套函数中时,“this”指针实际上指向全局“window”对象——这是我没想到的。我一定没有正确理解某些东西,因为我认为既然我在对象的函数中声明了嵌套函数,它应该具有与函数相关的“本地”范围(即“this”指针将引用对象本身,如在我的第一个“if”语句中是怎样的)。

任何指针(没有双关语)将不胜感激。

var std_obj = {
  options : { rows: 0, cols: 0 },
  activeEffect : "none",
  displayMe : function() {

    // the 'this' pointer is referring to the std_obj
    if (this.activeEffect=="fade") { }

    var doSomeEffects = function() {

      // the 'this' pointer is referring to the window obj, why?
      if (this.activeEffect=="fade") { }

    }

    doSomeEffects();   
  }
};

std_obj.displayMe();

【问题讨论】:

  • 你的问题到底是什么?
  • 在函数内部使用时,this 指的是调用函数的对象。
  • 你可以在外部范围内做的事情类似于var self = this;,然后通过闭包在内部函数中引用self
  • doSomeEffects 不与任何特定的 obj 相关联,因此 this 被假定为窗口,是所有元素之母。
  • @JoJoeDad 我该如何委婉地说这个?但是下面chuckj给出的答案到目前为止是您问题的答案。要真正了解发生了什么,您应该阅读execution contextscope chainthis keyword。从这里的一些答案的外观来看,其他人也应该阅读它们。我尝试宣传好的 javascript。这就是我花时间提供这些链接的原因。

标签: javascript function nested this


【解决方案1】:

在 JavaScript 中,this 对象实际上取决于您进行函数调用的方式。

一般有三种方式设置this对象:

  1. someThing.someFunction(arg1, arg2, argN)
  2. someFunction.call(someThing, arg1, arg2, argN)
  3. someFunction.apply(someThing, [arg1, arg2, argN])

在上述所有示例中,this 对象将是 someThing。 调用没有前导父对象的函数通常会得到 global 对象,在大多数浏览器中意味着 window 对象。

【讨论】:

  • 我根据您的回答将代码更改为this.doSomeEffects();,但仍然无法正常工作。为什么?
  • @Arashsoft this in this.doSomeEffects() 指向 std_obj。如上面的答案所述,如果一个函数没有对象引用,那么它需要 this 成为一个窗口对象。
【解决方案2】:

由于这似乎是同类问题中最受支持的问题之一,让我补充一下,经过这么多年,使用箭头函数的 ES6 解决方案:

var std_obj = {
  ...
  displayMe() {
    ...
    var doSomeEffects = () => {
                        ^^^^^^^    ARROW FUNCTION    
      // In an arrow function, the 'this' pointer is interpreted lexically,
      // so it will refer to the object as desired.
      if (this.activeEffect=="fade") { }
    };
    ...    
  }
};

【讨论】:

    【解决方案3】:

    this 不是闭包范围的一部分,它可以被认为是绑定在调用站点的函数的附加参数。如果方法不作为方法调用,则全局对象将作为this 传递。在浏览器中,全局对象等同于window。例如,考虑以下函数,

    function someFunction() {
    }
    

    和下面的对象,

    var obj = { someFunction: someFunction };
    

    如果您使用方法语法调用函数,例如,

    obj.someFunciton();
    

    然后this 绑定到obj

    如果直接调用someFunction(),比如,

    someFunction();
    

    然后this绑定到全局对象,即window

    最常见的解决方法是将其捕获到闭包中,例如,

    displayMe : function() {      
    
        // the 'this' pointer is referring to the std_obj      
        if (this.activeEffect=="fade") { }      
        var that = this;  
        var doSomeEffects = function() {      
    
          // the 'this' pointer is referring to global
          // that, however, refers to the outscope this
          if (that.activeEffect=="fade") { }      
        }      
    
        doSomeEffects();         
     }      
    

    【讨论】:

      【解决方案4】:

      要理解这个问题,请尝试获取以下 sn-p 的输出

      var myObject = {
          foo: "bar",
          func: function() {
              var self = this;
              console.log("outer func:  this.foo = " + this.foo);
              console.log("outer func:  self.foo = " + self.foo);
              (function() {
                  console.log("inner func:  this.foo = " + this.foo);
                  console.log("inner func:  self.foo = " + self.foo);
              }());
          }
      };
      myObject.func();
      

      以上代码将向控制台输出以下内容:

      outer func:  this.foo = bar
      outer func:  self.foo = bar
      inner func:  this.foo = undefined
      inner func:  self.foo = bar
      

      在外部函数中,this 和 self 都引用了 myObject,因此都可以正确引用和访问 foo。

      不过,在内部函数中,this 不再指向 myObject。结果, this.foo 在内部函数中是未定义的,而对局部变量 self 的引用仍然在范围内并且可以在那里访问。 (在 ECMA 5 之前,内部函数中的 this 将引用全局窗口对象;而在 ECMA 5 中,内部函数中的 this 将是未定义的。)

      【讨论】:

      • 在内部函数中,this指的是window对象(在浏览器环境中)或GLOBAL对象(在node.js环境中)
      • 为什么会这样?
      • @raneshu "use strict" 和 this 不再指代窗口
      【解决方案5】:

      附件变量和“this”之间存在差异。 “this”实际上是由函数的调用者定义的,而显式变量在称为外壳的函数声明块内保持不变。请看下面的例子:

      function myFirstObject(){
          var _this = this;
          this.name = "myFirstObject";
          this.getName = function(){
             console.log("_this.name = " + _this.name + " this.name = " + this.name);  
          }
      }
      
      function mySecondObject(){
          var _this = this;
          this.name = "mySecondObject";
          var firstObject = new myFirstObject();
          this.getName = firstObject.getName
      }
      
      var secondObject = new mySecondObject();
      secondObject.getName();
      

      你可以在这里试试: http://jsfiddle.net/kSTBy/

      你的函数中发生的是“doSomeEffects()”,被显式调用,这意味着上下文或函数的“this”是窗口。如果“doSomeEffects”是原型方法,例如this.doSomeEffects on say "myObject",然后 myObject.doSomeEffects() 会导致 "this" 成为 "myObject"。

      【讨论】:

      • 对于懒惰和不耐烦的人,这个演示日志:_this.name = myFirstObject this.name = mySecondObject
      【解决方案6】:

      正如 Kyle 所解释的,您可以使用 callapply 在函数中指定 this

      这是应用于您的代码的概念:

      var std_obj = {
          options: {
              rows: 0,
              cols: 0
          },
          activeEffect: "none",
          displayMe: function() {
      
              // the 'this' pointer is referring to the std_obj
              if (this.activeEffect == "fade") {}
      
              var doSomeEffects = function() {
                  // the 'this' pointer is referring to the window obj, why?
                  if (this.activeEffect == "fade") {}
              }
      
              doSomeEffects.apply(this,[]);
          }
      };
      
      std_obj.displayMe();
      

      JsFiddle

      【讨论】:

        【解决方案7】:

        我还收到一条警告“通过此方法对类字段的潜在无效引用访问

        class MyListItem {
            constructor(labelPrm) {
                this._flagActive = false;
                this._myLabel = labelPrm;
                labelPrm.addEventListener('click', ()=>{ this.onDropdownListsElementClick();}, false);
            }
        
            get myLabel() {
                return this._myLabel
            }
            get flagActive(){
                return this._flagActive;
            }
        
            onDropdownListsElementClick(){console.log("Now'this'refers to the MyListItem itself")}}//end of the class
        

        【讨论】:

          【解决方案8】:

          由于没有提到,我会提到使用.bind() 是一种解决方案 -

          
                  doSomeEffects=doSomeEffect.bind(this);
                  doSomeEffects();   
                }
              };
          
              std_obj.displayMe();
          

          这是一个更简单的例子 -

          bad = { 
            name:'NAME', 
            z : function() { 
              function x() { console.log(this.name); }; 
              x() 
            } 
          };
          bad.z() // prints 'undefined'
          
          good = { 
            name:'NAME', 
            z : function() { 
              function x() { console.log(this.name); }; 
              x=x.bind(this);
              x();
            } 
          };
          good.z() // prints 'NAME'
          
          

          确实,使用箭头函数=> 看起来更流畅并且对程序员来说很容易。但是,应该记住,与通过.bind() 简单地将函数的this 与指针相关联相比,词法作用域可能需要更多的处理和内存工作来设置和维护该词法作用域。

          在 JS 中开发类的部分好处是提供了一种方法,使this 更可靠地呈现和可用,从而摆脱函数式编程和词法范围,从而减少开销。

          From MDN

          性能注意事项 如果某个特定任务不需要闭包,则在其他函数中不必要地创建函数是不明智的,因为它会在处理速度和内存消耗方面对脚本性能产生负面影响。

          【讨论】:

            【解决方案9】:

            这是因为“this”指的是self对象/本地函数。

            var std_obj = {
              options : { rows: 0, cols: 0 },
              activeEffect : "none",
              displayMe : function() {
            
                if (this.activeEffect=="fade") { }
            
                let This = this; // 'this' here is for the std_obj scope. Create a reference to 'this' if you want to use it elsewhere.
            
                var doSomeEffects = function() {
                  // 'this' here refers to the doSomeEffects scope. If you don't want "this," you can still use "this" of the std_obj scope.
            
                  if (This.activeEffect=="fade") { }
            
                }
            
                doSomeEffects();   
              }
            };
            
            std_obj.displayMe();
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2010-09-07
              • 2011-06-03
              相关资源
              最近更新 更多