Javascript "this" pointer within nested function

I have a question concerning how the "this" pointer is treated in a nested function scenario.

Say I insert this following sample code into a web page. I get an error when I call the nested function "doSomeEffects()". I checked in Firebug and it indicates that when I am in that nested function, the "this" pointer is actually pointing to the global "window" object - which I did not expect. I must not be understanding something correctly because I thought since I declared the nested function within a function of the object, it should have "local" scope in relation to the function (i.e. the "this" pointer would be referring to the object itself like how it is in my first "if" statement).

Any pointers (no pun intended) would be appreciated.

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();
61300 次浏览

在 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”是有区别的。“ 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/

在你的函数中发生的事情就是“ doSome effect ()”,被明确地调用,这意味着上下文或者函数的“ this”是窗口。如果“ doSome effect”是一个原型方法,例如 this. doSome effect 对于 say“ myObject”,那么 myObject.doSome effect ()将导致“ this”成为“ myObject”。

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

function someFunction() {
}

以及下面的物体,

var obj = { someFunction: someFunction };

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

obj.someFunciton();

那么 this绑定到 obj

如果直接调用 some function () ,例如,

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

正如 Kyle 所解释的,你可以使用 call或者 apply在函数中指定 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

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

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中,这在内部函数中是未定义的。)

Since this appears to be among the most upvoted questions of its kind, let me add, after all these years, the ES6 solution using arrow functions:

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") { }
};
...
}
};

我还收到了“ 对类字段的引用访问可能无效”的警告

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

Since it wasn't mentioned I will mention that using .bind() is a solution -


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'


It is true that using an arrow function => looks slicker and is easy for the programmer. However, it should be kept in mind that a lexical scope is likely to require more work in terms of processing and memory to setup and maintain that lexical scope, compared to simply associating a function's this with a pointer via .bind().

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

MDN 的

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

It's because "this" refers to the self object / local function.

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