Variable scope in nested functions in Javascript

元气小坏坏 提交于 2019-12-24 03:53:07

问题


I have looked through countless examples which indicate that this is supposed to work, but it does not. I was wondering if someone could have a look and indicate why. I am trying to access the variable "dia" from within the setTimeout function, but it always returns 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);
}

回答1:


There are two problems:

The first is that the function you're passing into setTimeout has an enduring reference to the dia variable, not a copy of dia's value as of when the function was created. So when the functions run, they all see the same value for dia, which is the value it has then, after the loop is complete.

This example may help make this clearer:

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

The code above shows us "2" twice. It does not show us "1" and then "2". Both functions access a as it is when they run.

If you think about it, this is exactly how global variables work. And in fact, there's a reason for that: It's exactly the way global variables work. :-)

More: Closures are not complicated

Now, sometimes, you want to get a copy of dia's value as of when the function was created. In those cases, you usually use a builder function and pass dia to it as an argument. The builder function creates a function that closes over the argument, rather than 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();
    };
}

Because the function buildFunction returns closes over d, which doesn't change, rather than dia, which does, it gives us the value as of when it was created.

The second problem is that your loop condition is incorrect, which is why you're seeing undefined. Your loop is:

for(dcount = 0; dcount <= dialogue.length; dcount++)   {

There is no element at dialogue[dialogue.length]. The last element is at dialogue[dialogue.length - 1]. You should be exiting your loop with < dialogue.length, not <= dialogue.length. With < dialogue.length, you'd still have a problem: dia would always be the last entry (see above), but at least it wouldn't be undefined.




回答2:


try this

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);
}

This solution just pass an argument to the setTimeout function so it can take the array index from there and take the correct item



来源:https://stackoverflow.com/questions/18067742/variable-scope-in-nested-functions-in-javascript

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!