Javascript';这';值变化,但可以';我不明白为什么

Javascript';这';值变化,但可以';我不明白为什么,javascript,Javascript,我完全是一个Javascript新手,我正试着把我的头绕到奥尔恩身上。我遇到的是,当从同一对象上的另一个方法调用对象方法时,被调用方法中“this”的局部值的值正在改变。这是我的密码: var generator = { generateForLevelSkillAndCount : function(level, skill, count) { var functionCall = this['generate_' + level + '_' + skill];

我完全是一个Javascript新手,我正试着把我的头绕到奥尔恩身上。我遇到的是,当从同一对象上的另一个方法调用对象方法时,被调用方法中“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\u 0\u 4(count)
。然而,这就是它失败的地方,Chrome的Javascript控制台告诉我“UncaughtTypeError:Object[Object DOMWindow]没有“generate\u generic\u dots”方法。”

我知道问题在于
generate_0_4
中的
this
的值是一个DOMSWindow对象,而不是生成器(这就是
this
generateForSkillLevelAndCount
中所指的),但我不明白为什么会发生这种情况


更新:我根据CMS的建议更新了示例代码,以消除
eval
,但返回了相同的错误,因此这不仅仅是
eval
错误。

首先,我鼓励您在不需要eval的地方避免eval,例如,在First函数中:

//...
generateForLevelSkillAndCount : function(level, skill, count) {
    var functionCall = this['generate_' + level + '_' + skill];
    return functionCall(count);
},
//...
function bind(func, obj) {
    return function() {
        func.apply(obj, arguments);
    }
}
您可以使用括号表示法属性访问器来代替
eval
,在这种情况下不需要它

现在,我猜您正在Chrome的控制台上尝试您的代码,
eval
失败,因为控制台有一个bug,当从FunctionExpression调用
eval
(例如
generateForLevelSkillAndCount
)时,被评估的代码使用其变量环境和词汇环境的全局上下文

有关此错误的更多信息,请参阅

编辑:重新读取代码后,出现问题的原因是将函数分配给
functionCall
变量时丢失了基本对象引用,您可以:

直接调用函数,而不使用该变量:

//...
generateForLevelSkillAndCount : function(level, skill, count) {
    this['generate_' + level + '_' + skill](count);
},
//...
或者仍然使用变量,但:


更多信息

我和你一样困惑不解,但是你是否考虑过如果你显式地调用
生成\u 0\u 4
而不是通过
eval()
解析它会发生什么情况?

当你动态地调用
生成\u 0\u 4
时(使用隐式
来生成字符串()
)它作为一个特殊函数返回到
generateForLevelSkillAndCount
。因为它位于
窗口的
范围而不是
对象的
范围内,所以它不能引用
这个
,内部调用失败,因为
这个
在该上下文中不存在

下面是如何查看正在发生的事情:

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]
您可以使用以下命令控制方法调用的执行上下文:


在JavaScript中,上下文对象(
)设置为“全局对象”(
窗口,在浏览器中),除非该方法作为对象属性访问。因此:

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
作为其上下文对象。有两种方法可以解决此问题:将方法作为对象属性访问或使用或:


这是Javascript的一个特性:
This
的值取决于调用函数的对象,而不是函数的定义位置。(当函数本身是一级对象时,这是有意义的。)
在大多数其他上下文中,This
指的是窗口对象

使用包装器函数有两种常见的解决方法:

//...
generateForLevelSkillAndCount : function(level, skill, count) {
    var functionCall = this['generate_' + level + '_' + skill];
    return functionCall(count);
},
//...
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);


这就可以了。

谢谢你提供的信息。我没有意识到我可以使用这种表示法(尽管考虑到Javascript对象的性质,这是有意义的)。不幸的是,更新代码后出现了同样的错误(我也会更新问题中的示例代码)。如果使用括号表示法属性访问器以同样的方式失败,是否有其他方法来执行动态函数调用?@John Biesnecker是的,但您真的需要它们吗?似乎您可以轻松地使用常规函数和/或查找表,而不是将
3
硬编码为
generate\u 0\u 4()
--除非有很多经过编辑的代码。@banzaimonkey说“做一些很酷的东西并返回它”非常复杂,而且调用了很多次,但我当然可以用查找表来代替。不过,大多数情况下,它是“这必须是可能的!”关于未来的问题。@CMS,非常感谢您提供更多信息的链接。
这是一只神秘的野兽……:)当我直接调用
生成0\u 4
时,它会工作。但是,在摆脱了CMS的答案后,仍然没有骰子。啊,动态范围界定的乐趣!太好了,这正是我需要的。JS的对象语法的确切工作原理对我来说仍然是个谜,但是你所建议的方法非常有效。谢谢所以foo.bar()相当于bar.call(foo)?@justin-在我的示例中,是的。(实际上,函数在当前作用域中的名称可能与其作为对象属性的名称相匹配,也可能不匹配,但您知道了。)谢谢您的回答,Dave。对不起,我比你先到了本的楼上,他们都在工作。:)
var functionCall = this['generate_' + level + '_' + skill];
return functionCall(count);
this['generate_' + level + '_' + skill](count);