这(如下)最终给了我一个“超出最大调用堆栈大小”错误。这似乎是由于“this”在“this.actions”对象中的解释方式造成的。在该对象中,“this”是指该对象还是 Unit 类的实例?如果是前者,将 .bind(this) 放在“this.actions”对象的末尾会使“this”引用类实例吗?如果是这样,为什么?如果没有,为什么不呢?
function Unit(){
this.move = function(direction){
switch(direction){
case 'up': { console.log('foo'); break; }
case 'down': { console.log('foooo'); break; }
}
console.log('bar');
}
this.shoot = function(){console.log('zap')}
this.actions = {
'moveUp' : function(){ this.move('up') },
'moveDown' : function(){ this.move('down') },
'shoot' : function(){ this.shoot() }
}
return this
}
this
对象中的关键字actions
将引用actions
对象。
一些可能的修复可能如下所示:
function Unit(){
var self = this;
this.move = function(direction){
switch(direction){
case 'up': { console.log('foo'); break; }
case 'down': { console.log('foooo'); break; }
}
console.log('bar');
}
this.shoot = function(){console.log('zap')}
this.actions = {
'moveUp' : function(){ this.move('up') }.bind(self),
'moveDown' : function(){ this.move('down') }.bind(self),
'shoot' : function(){ this.shoot() }.bind(self)
}
return this
}
或者,当您调用这些方法时,您可以使用
call
或 apply
例如:
var coolUnit = new Unit();
Unit.actions.moveUp.call(coolUnit);
在对象的背景下理解
this
需要一些工作,但这里有一些资源:
http://unschooled.org/2012/03/understanding-javascript-this/
http://javascriptissexy.com/understand-javascripts-this-with-clarity-and-master-it/
TL;DR - 您可以使用一系列心理“规则”来帮助跟踪给定上下文中的
this
是什么。例如。 left-of-the-dot 规则,其中“点”左侧的对象获得 this
绑定。
Object.foo() <- `this` in the method `foo` will point to `Object`
使用上面提到的“规则”,您可以合理地认为
new Unit.actions.moveUp()
将 this
绑定设置为指向操作对象,因为它是 left-of-the-dot。
或者您可以使用
call
/bind
/apply
将 this
绑定到您想要的上下文,如上所示。
使用绑定
原因:
可以说:
var r = new Unit();
当你调用 r.actions.moveup() 时,moveup 函数中传入的 'this' 是 actions。
function Unit(){
this.move = function(direction){
switch(direction){
case 'up': { console.log('foo'); break; }
case 'down': { console.log('foooo'); break; }
}
console.log('bar');
}
this.shoot = function(){console.log('zap')}
this.actions = {
'moveUp' : function(){ this.move('up') }.bind(this),
'moveDown' : function(){ this.move('down') }.bind(this),
'shoot' : function(){ this.shoot() }.bind(this)
}
return this
}
另一种解决方案:
this
不是变量。嵌套函数可以访问“父”函数(闭包)中定义的所有变量——您可以创建变量,为其分配 this
,并使用它而不是 this
,它会执行您想要和期望的操作:
function Unit(){
var self = this;
self.move = function(direction){
switch(direction){
case 'up': { console.log('foo'); break; }
case 'down': { console.log('foooo'); break; }
}
console.log('bar');
}
self.shoot = function(){console.log('zap')}
self.actions = {
'moveUp' : function(){ self.move('up') },
'moveDown' : function(){ self.move('down') },
'shoot' : function(){ self.shoot() }
}
return self
}
最近我遇到了这篇文章关于这个。
在第 4.2 节中,他使用了一个与您的代码类似的示例,并强调了忘记“new”的陷阱。函数调用中的“this”指向全局对象(窗口或浏览器),因此当您返回它时,您将函数放在全局对象上,然后返回对其的引用。如果您使用 new Unit() 并且不返回它,您将获得一个带有您的函数的对象。
你可以使用bind,但我认为它会类似于Unit.bind(Unit),这看起来很奇怪。
您还可以使用工厂函数来返回对象,并且您不必担心忘记 new
function Unit(){
var move = function(direction){
switch(direction){
case 'up': { console.log('foo'); break; }
case 'down': { console.log('foooo'); break; }
}
console.log('bar');
};
var shoot = function(){console.log('zap')};
var actions = {
moveUp : function(){ move('up') },
moveDown : function(){ move('down') },
shoot : shoot
};
return {
actions:actions
};
}
var player1=Unit();
player1.actions.shoot();
player1.actions.moveDown();
player1.actions.moveUp();
我从 move 的调用中删除了 this ,部分问题是 actions 是一个对象而不是函数,所以虽然你可以在对象内部的函数上使用绑定,但你可以像我在代码中那样,只需关闭函数然后公开它。
此外,如果您使用 self=this 并且不使用 new 关键字,您仍然可以引用窗口/浏览器对象并创建全局范围。