嵌套函数中的Javascript“ this”指针


94

我有一个关于在嵌套函数方案中如何处理“ 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();

您到底要问什么?
Sarfraz 2012年

2
在函数内部使用时,this指在其上调用函数的对象。
2012年

8
您可以在外部范围中执行的操作类似var self = this;,然后self通过闭包在内部函数中进行引用。
2012年

1
doSomeEffects没有与任何obj特别关联,因此this假定它是窗口,所有元素的母亲。
2012年

3
@JoJoeDad我该如何用外交方式说呢?但是,下面的chuckj给出的答案是您问题的答案。要真正了解发生了什么,您应该阅读执行上下文作用域链此关键字。从这里的一些答案看,其他人也应该阅读它们。我尝试宣传好的javascript。这就是为什么我花时间给出这些链接。
onefootswill

Answers:


120

在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。在没有前置父对象的情况下调用函数通常会为您提供全局对象,这在大多数浏览器中都意味着该window对象。


this.doSomeEffects();根据您的回答将代码更改为,但仍然无法正常工作。为什么?
Arashsoft

1
@Arashsoft thisthis.doSomeEffects()std_obj。正如上面答案中所解释的,如果一个函数没有对象引用,则它必须this是一个窗口对象。
Shivam

38

由于这似乎是同类问题中最受争议的问题之一,因此,多年来,让我添加使用箭头功能的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") { }
    };
    ...    
  }
};

5
现在,这就是进步!
约书亚·品特

优秀,简单的解决方案。
约书亚·施利希廷

32

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

那=这很完美
Nather Webber

10

附件变量和“ 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”是原型方法,例如说“ myObject”的this.doSomeEffects,则myObject.doSomeEffects()将导致“ this”为“ myObject”。


4
对于懒惰和不耐烦的人,此演示记录:_this.name = myFirstObject this.name = mySecondObject
ptim

9

要理解此问题,请尝试获取以下代码段的输出

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。

但是,在内部函数中,它不再引用myObject。结果,this.foo在内部函数中未定义,而对局部变量self的引用仍然在作用域中,并且可以在该处访问。(在ECMA 5之前,内部函数中的此对象将引用全局窗口对象;而从ECMA 5开始,内部函数中的此对象将是未定义的。)


1
在内部函数中,this引用窗口对象(在浏览器环境中)或GLOBAL对象(在node.js环境中)
raneshu 2016年

2
为什么会这样呢?
setu

@raneshu“使用严格”,this不再指窗口
rofrol

3

如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


0

我还收到一条警告“ 通过此字段可能对类字段的引用无效

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

0

既然没有提到,我会提到使用.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'

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

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

从MDN

性能考虑如果特定任务不需要关闭,则不必要在其他函数中创建函数是不明智的,因为这将在处理速度和内存消耗方面对脚本性能产生负面影响。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.