Javascript”;这个“;嵌套函数中的指针

Javascript "this" pointer within nested function

本文关键字:函数 指针 这个 Javascript 嵌套      更新时间:2023-09-26

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

在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对象。

由于这似乎是同类问题中投票率最高的问题之一,经过这么多年,让我补充一下使用箭头函数的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") { }
    };
    ...    
  }
};

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

function someFunction() {
}

以及以下对象

var obj = { someFunction: someFunction };

如果使用方法语法(如)调用函数

obj.someFunciton();

则CCD_ 12与CCD_。

如果直接调用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();         
 }      

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

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之前,内部函数中的this指的是全局窗口对象;而在ECMA五之前,内部功能中的this是未定义的。)

外壳变量和"this"之间有区别。"this"实际上是由函数的调用程序定义的,而显式变量在称为enclosure的函数声明块中保持不变。参见以下示例:

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对say"myObject",那么myObject.doSomeEffects()将导致"this"为"myObject)。

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

由于没有提到,我将提到使用.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更可靠地呈现和可用,摆脱函数式编程和词法范围,从而减少开销。

来自MDN

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

这是因为"这个";指自对象/局部函数。

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

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

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