Javascript中嵌套函数的变量范围

时间:2013-08-05 21:08:05

标签: javascript function variables scope nested

我查看了无数的例子,表明这应该有用,但事实并非如此。我想知道是否有人可以看一看并说明原因。我试图从setTimeout函数中访问变量“dia”,但它总是返回undefined:

var dialogue = new Array();
dialogue[0] = 'Hi there Mo, I am Mark. I will be guiding through the game for you today';
dialogue[1] = 'Hey there Mark, how you doing?';
dialogue[2] = 'I am doing fine sweetie pie, how about yourself?';
dialogue[3] = 'I am good too, thanks. Are you ready for today, i.e. the big day?';
dialogue[4] = 'I certainly am, Mark';
var dcount;
var loopDelay;
var diatext;
for(dcount = 0; dcount <= dialogue.length; dcount++)   {
    var dia = dialogue[dcount];
    if(dcount == 0) { loopDelay = 0; } else {
        loopDelay = ((dia.length)*1000)/18;
    }
    setTimeout(function() {
        alert(dia);
        diatext = Crafty.e('2D, DOM, Text')
            .text(dia)
            .textFont({ size: '11px', weight: 'bold' })
            .attr({ x: 200, y: 150, w:400, h:300})
            .css();
    }, loopDelay);
}

2 个答案:

答案 0 :(得分:11)

有两个问题:

首先,您传入setTimeout的函数对dia变量持久参考 a 在创建函数时复制dia的值。因此,当函数运行时,它们都会看到dia的相同值,即在循环完成后它具有然后的值。

这个例子可能有助于使这一点更加清晰:

var a = 1;
setTimeout(function() {
    alert(a);
}, 0);
a = 2;
setTimeout(function() {
    alert(a);
}, 0);

上面的代码两次显示“2”。它不会向我们显示“1”然后显示“2”。这两个函数在运行时访问a

如果你考虑一下,这正是全局变量的工作原理。事实上,这是有原因的:它正是全局变量的工作方式。 : - )

更多:Closures are not complicated

现在,有时候,您希望在创建函数时获得dia值的副本。在这些情况下,您通常使用构建器函数并将dia作为参数传递给它。构建器函数创建一个关闭参数的函数,而不是dia

for(dcount = 0; dcount <= dialogue.length; dcount++)   { // But see note below about <=
    var dia = dialogue[dcount];
    if(dcount == 0) { loopDelay = 0; } else {
        loopDelay = ((dia.length)*1000)/18;
    }
    setTimeout(buildFunction(dia), loopDelay);
}
function buildFunction(d) {
    return function(d) {
        alert(d);
        diatext = Crafty.e('2D, DOM, Text')
            .text(d)
            .textFont({ size: '11px', weight: 'bold' })
            .attr({ x: 200, y: 150, w:400, h:300})
            .css();
    };
}

因为函数buildFunction返回d而不是dia,而不是undefined,它会为我们提供创建时的值。< / p>

第二个问题是您的循环条件不正确,这就是您看到for(dcount = 0; dcount <= dialogue.length; dcount++) { 的原因。你的循环是:

dialogue[dialogue.length]

dialogue[dialogue.length - 1]没有元素。最后一个元素位于< dialogue.length。您应该使用<= dialogue.length退出循环,而不是< dialogue.length。使用dia,您仍然会遇到问题:{{1}}始终是最后一个条目(见上文),但至少它不会是未定义的。

答案 1 :(得分:1)

试试这个

var dialogue = new Array(); 
dialogue[0] = 'Hi there Mo, I am Mark. I will be guiding through the game for you today';
dialogue[1] = 'Hey there Mark, how you doing?';
dialogue[2] = 'I am doing fine sweetie pie, how about yourself?';
dialogue[3] = 'I am good too, thanks. Are you ready for today, i.e. the big day?';
dialogue[4] = 'I certainly am, Mark';
var dcount;
var loopDelay;
var diatext;
for(dcount = 0; dcount < dialogue.length; dcount++)   {
    var dia = dialogue[dcount];
    if(dcount == 0) { loopDelay = 0; } else {
    loopDelay = ((dia.length)*1000)/18;
}
setTimeout(function(count) {
    alert(dialogue[count]);

}, loopDelay,dcount);
}

这个解决方案只是将一个参数传递给setTimeout函数,这样就可以从那里获取数组索引并获取正确的项