我是一个完整的Javascript newb,我正试图围绕OLN。我遇到的是,当从同一个对象上的另一个方法调用一个对象方法时,被调用方法中'this'的本地值的值正在改变。这是我的代码:
var generator = {
generateForLevelSkillAndCount : function(level, skill, count) {
var functionCall = this['generate_' + level + '_' + skill];
return functionCall(count);
},
generate_0_4 : function(count) {
return this.generate_generic_dots(count, 3);
},
generate_generic_dots : function(count, maxDots) {
/* do cool stuff and return it */
}
};
所以,我致电generator.generateForLevelSkillAndCount(0, 4, 20)
并且它正常工作,呼叫generate_0_4(count)
。然而,这是失败的地方,Chrome的Javascript控制台告诉我“未捕获的TypeError:对象[对象DOMWindow]没有方法'generate_generic_dots'。”
我知道问题是this
中generate_0_4
的值是DOMWindow对象,而不是生成器(this
指向generateForSkillLevelAndCount
的值eval
1}}但我无法弄清楚为什么会发生这种情况。
更新:我根据CMS的建议更新了示例代码以摆脱eval
,但是返回了同样的错误,因此它不仅仅是{{1}}错误。
答案 0 :(得分:8)
在JavaScript中,上下文对象(this
)在浏览器中设置为“全局对象”(window
,除非该方法作为对象属性进行访问。因此:
var foo = { bar: function() { alert(this.baz); }, baz: 5 };
var bar = foo.bar;
var baz = 3;
foo.bar(); // alerts 5, from foo
foo["bar"](); // alerts 5, from foo
bar(); // alerts 3, from the global object
请注意,所有三个函数调用都是完全相同的函数!
因此,在您的代码中,您将所需的方法分配给functionCall
并直接调用它,这会导致函数使用window
作为其上下文对象。有两种方法:将方法作为对象属性访问,或使用.call()
或.apply()
:
function generateForLevelSkillAndCount1(level, skill, count) {
return this['generate_' + level + '_' + skill](count);
}
function generateForLevelSkillAndCount2(level, skill, count) {
var functionCall = this['generate_' + level + '_' + skill];
return functionCall.call(this, count);
}
答案 1 :(得分:3)
首先,我鼓励你避免在你不需要它的地方,例如,在你的拳头功能中:
//...
generateForLevelSkillAndCount : function(level, skill, count) {
var functionCall = this['generate_' + level + '_' + skill];
return functionCall(count);
},
//...
您可以使用括号表示法属性访问器代替eval
,在这种情况下不需要。
现在,我猜你正在Chrome控制台上试用你的代码,eval
失败,因为当从FunctionExpression调用eval
时,控制台有错误(例如generateForLevelSkillAndCount
}),评估的代码使用全局上下文作为其变量环境和词汇环境。
有关此错误的详细信息,请参阅this answer。
编辑:重新读取代码后,问题就出现了,因为在将函数分配给functionCall
变量时丢失了基础对象引用,您可以:
直接调用函数,而不使用该变量:
//...
generateForLevelSkillAndCount : function(level, skill, count) {
this['generate_' + level + '_' + skill](count);
},
//...
或仍然使用您的变量,但persist the this
value:
//...
generateForLevelSkillAndCount : function(level, skill, count) {
var functionCall = this['generate_' + level + '_' + skill];
return functionCall.call(this, count);
},
//...
更多信息on this
...
答案 2 :(得分:1)
您可以使用call():
来控制方法调用的执行上下文var generator = {
generateForLevelSkillAndCount : function(level, skill, count) {
return this['generate_' + level + '_' + skill].call(this, count);
},
generate_0_4 : function(count) {
return this.generate_generic_dots.call(this, count, 3);
},
generate_generic_dots : function(count, maxDots) {
/* do cool stuff and return it */
}
};
答案 3 :(得分:0)
我和你一样困惑,但你是否考虑过如果你明确地调用generate_0_4
而不是通过eval()
解析会发生什么?
答案 4 :(得分:0)
当您动态调用generate_0_4
时(使用隐式to_string()
),它将作为ad-hoc函数返回到generateForLevelSkillAndCount
。由于它位于Window
范围而不是Object
范围内,因此无法引用this
,内部调用失败,因为在该上下文中不存在this
。
以下是如何查看正在发生的事情:
generate_0_4 : function(count) {
throw(this);
return this.generate_generic_dots(count, 3);
},
使用generator.generateForLevelSkillAndCount(0, 4, 1);
,您可以获得[object Window]
或[object DOMWindow]
。
使用generator.generate_0_4(1);
,您可以得到您期望的内容(以及有效的内容):[object Object]
或#<an Object>
。
答案 5 :(得分:0)
这是Javascript的一个特性:this
的值取决于调用函数的对象,而不是它定义的位置。 (当函数本身就是第一类对象时,这是有道理的。)在大多数其他上下文中,this
指的是窗口对象。
使用包装函数有两种常见的解决方法:
function bind(func, obj) {
return function() {
func.apply(obj, arguments);
}
}
或使用闭包:
var self = this;
function generate_blah() {
// use self instead of this here
}
在你的情况下,只需更换
var functionCall = this['generate_' + level + '_' + skill];
return functionCall(count);
与
this['generate_' + level + '_' + skill](count);
会做到这一点。