我有一个关于如何在嵌套函数场景中处理“this”指针的问题。
假设我将以下示例代码插入到网页中。当我调用嵌套函数“doSomeEffects()”时出错。我检查了Firebug,它表明当我在嵌套函数中时,“this”指针实际上指向全局“窗口”对象 - 我没想到。我一定不能理解正确的东西,因为我认为既然我在对象的函数中声明了嵌套函数,它应该具有与函数相关的“局部”范围(即“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();
}
};
std_obj.displayMe();
答案 0 :(得分:108)
在JavaScript中,this
对象实际上是基于如何进行函数调用。
通常,有三种方法可以设置this
对象:
someThing.someFunction(arg1, arg2, argN)
someFunction.call(someThing, arg1, arg2, argN)
someFunction.apply(someThing, [arg1, arg2, argN])
在上述所有示例中,this
对象将为someThing
。
调用没有前导父对象的函数通常会获得 global 对象,在大多数浏览器中这意味着window
对象。
答案 1 :(得分:28)
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();
}
答案 2 :(得分: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”是原型方法,例如this.doSomeEffects on say“myObject”,然后myObject.doSomeEffects()会导致“this”成为“myObject”。
答案 3 :(得分:5)
要了解此问题,请尝试获取以下代码段的输出
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开始,内部函数中的这将是未定义的。)
答案 4 :(得分:3)
正如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();
答案 5 :(得分:3)
您也可以通过.bind()方法
来完成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() {
// now 'this' pointer is referring to the std_obj when calling by bound function
if (this.activeEffect=="fade") { }
alert(this.activeEffect);
}
var newBoundFunction= doSomeEffects.bind(std_obj);
newBoundFunction();
}
};
我们实际上可以使用call(),bind()和apply()显式设置它的值。这三者非常相似,但了解细微差别非常重要。
立即调用Call和Apply。调用需要任意数量的参数:this,后跟其他参数。 Apply只接受两个参数:this,后跟一个附加参数数组。
你还跟着我吗?一个例子应该使这更清楚。看下面的代码。我们正试图添加数字。将其复制到浏览器控制台并调用该函数。
function add(c, d) {
console.log(this.a + this.b + c + d);
}
add(3,4);
// logs => NaN
添加功能记录NaN(不是数字)。那是因为this.a和this.b未定义。它们不存在。并且您无法为未定义的内容添加数字。
让我们向等式引入一个对象。我们可以使用call()和apply()来调用我们的对象函数:
function add(c, d) {
console.log(this.a + this.b + c + d);
}
var ten = {a: 1, b: 2};
add.call(ten, 3, 4);
// logs => 10
add.apply(ten, [3,4]);
// logs => 10
当我们使用add.call()时,第一个参数应该绑定到这个参数。后续参数将传递给我们调用的函数。因此,在add()中,this.a引用ten.a,this.b引用ten.b,我们得到1 + 2 + 3 + 4返回,或10。
add.apply()是类似的。第一个参数是应该绑定的内容。后续参数是要在函数中使用的参数数组。
Bind怎么样? bind()中的参数与call()相同,但不立即调用bind()。相反,bind()返回一个具有此绑定上下文的函数。因此,当我们事先不知道所有参数时,bind()很有用。再一次,一个例子应该有助于你的理解:
var small = {
a: 1,
go: function(b,c,d){
console.log(this.a+b+c+d);
}
}
var large = {
a: 100
}
将上述内容复制到您的控制台中。然后拨打
small.go(2,3,4);
// logs 1+2+3+4 => 10
冷却。这里没什么新鲜的。但是,如果我们想要使用large.a的值呢?我们可以使用call / apply:
small.go.call(large,2,3,4);
// logs 100+2+3+4 => 109
现在,如果我们还不知道所有3个参数呢?我们可以使用bind:
var bindTest = small.go.bind(large,2);
如果我们上面的console.log我们的变量bindTest,我们可以看到我们正在使用的
console.log(bindTest);
// logs => function (b,c,d){console.log(this.a+b+c+d);}
请记住,使用bind返回已经有此绑定的函数!所以我们已经成功地绑定了我们的大对象。我们已经将第二个参数传递给了数字2.后来,当知道了其余的参数时,我们可以将它们传递给它们:
bindTest(3,4);
// logs 100+2+3+4 => 109
为清楚起见,这里是一个块中的所有代码。仔细查看,并将其复制到您的控制台,以真正了解正在发生的事情!
var small = {
a: 1,
go: function(b,c,d){
console.log(this.a+b+c+d);
}
}
var large = {
a: 100
}
small.go(2,3,4);
// logs 1+2+3+4 => 10
var bindTest = small.go.bind(large,2);
console.log(bindTest);
// logs => function (b,c,d){console.log(this.a+b+c+d);}
bindTest(3,4);
// logs 100+2+3+4 => 109
请记住以下几点:
这个值通常由函数执行上下文决定。
在全局范围内,这指的是全局对象(窗口对象)。
当使用new关键字(构造函数)时,它绑定到正在创建的新对象。
我们可以使用call(),bind()和apply()来显式设置它的值。
箭头函数不绑定它 - 而是以词法方式绑定(即基于原始上下文)
答案 6 :(得分: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
答案 7 :(得分: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'
确实,使用箭头功能=>
看起来更流畅,并且对于程序员来说很容易。但是,应该记住,与简单地通过{{1将函数的this
与指针关联起来相比,词汇范围可能需要更多的处理和内存工作来建立和维护该词汇范围。 }}。
在JS中开发类的部分好处是,提供了一种方法,使.bind()
更可靠地存在和可用,从而摆脱了函数编程和词法范围,从而减少了开销。
性能注意事项 如果不需要为特定任务关闭文件,则在其他函数中不必要地创建函数是不明智的,因为这会在处理速度和内存消耗方面对脚本性能产生负面影响。