Javascript中嵌套函数中的变量范围

Javascript中嵌套函数中的变量范围,javascript,function,variables,scope,nested,Javascript,Function,Variables,Scope,Nested,我看过无数的例子,这些例子表明这应该是可行的,但事实并非如此。我想知道是否有人可以看一下并说明原因。我试图从setTimeout函数中访问变量“dia”,但它总是返回未定义的: 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 doin

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

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);
}
var对话=新数组();
对话[0]=“你好,莫,我是马克。今天我将为你指导整个游戏';
对话[1]=“嘿,马克,你好吗?”;
对话[2]=“我做的很好,甜心馅饼,你呢?”;
对话[3]=“我也很好,谢谢。你准备好迎接今天,也就是重要的一天了吗;
对话[4]=“我当然是,马克”;
var数据计数;
无功循环延迟;
虚拟文本;

对于(dcount=0;dcount有两个问题:

第一个是传递给
setTimeout
的函数对
dia
变量有一个持久的引用,而不是
dia
在函数创建时的值的副本。因此,当函数运行时,它们都会看到
dia
的相同值,这是在循环已完成

此示例可能有助于更清楚地说明这一点:

var a = 1;
setTimeout(function() {
    alert(a);
}, 0);
a = 2;
setTimeout(function() {
    alert(a);
}, 0);
上面的代码向我们显示了两次“2”。它没有向我们显示“1”和“2”。这两个函数在运行时都按原样访问
a

仔细想想,这就是全局变量的工作方式。事实上,这是有原因的:全局变量就是这样工作的。:-)

更多:

现在,有时,您希望获得函数创建时的
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();
    };
}
对话[dialogue.length]
中没有任何元素。最后一个元素位于
对话[dialogue.length-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);
}
var对话=新数组();
对话[0]=“你好,莫,我是马克。今天我将为你指导整个游戏';
对话[1]=“嘿,马克,你好吗?”;
对话[2]=“我做的很好,甜心馅饼,你呢?”;
对话[3]=“我也很好,谢谢。你准备好迎接今天,也就是重要的一天了吗;
对话[4]=“我当然是,马克”;
var数据计数;
无功循环延迟;
虚拟文本;
对于(dcount=0;dcount

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

只是稍微澄清一下,当您没有像TJ那样关闭'dia'时看到“undefined”的原因是,在超时发生之前,您的循环完成了,并且您永远无法访问对话[dialog.length+1],这超出了范围。@MaxPRafferty:实际上,
dia
没有引用
dialog[dialog.length+1]
,它引用了
dialog[dialog.length]
。但这仍然超出了范围。感谢您的详细解释!@T.J.Crowder啊,很好的回答!看起来我比自己稍微超前了一点。我的意思是循环的结尾总是超出其定义的范围+1,即dialog.length。请注意,返回函数(在“返回函数(d)”行上){'此时实际上并不需要参数'd'。buildFunction本身的闭包可以完成这项工作。另外,我认为您也不应该在此处填写此参数。原因是您可能使用的模式期望该参数是其他参数。完美的例子是addEventListener func作为参数传入的选项:在这里,函数只能接受一个参数,并且始终是调用方事件类型。
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);
}