2013-08-05 2 views
2

Я просмотрел множество примеров, которые указывают, что это должно работать, но это не так. Мне было интересно, может ли кто-нибудь взглянуть и указать, почему. Я пытаюсь получить доступ к переменной «диа» из функции SetTimeout, но она всегда возвращает неопределенное значение:Область переменной в вложенных функциях в Javascript

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

ответ

10

Есть две проблемы:

Первое, что функция вы передаете в setTimeout имеет стойкую ссылку к переменной dia, некопию значения dia «s, как, когда была создана функция. Поэтому, когда функции запускаются, все они видят одинаковое значение для dia, то есть значение, которое оно имеет , затем, после того, как цикл завершен.

Этот пример может помочь сделать это более ясным:

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

Код выше показывает нам "2" дважды. Он не показывает нам «1», а затем «2». Обе функции получают доступ к a, как это происходит, когда они run.

Если вы думаете об этом, это именно то, как работают глобальные переменные. И на самом деле, есть причина для этого: именно так работают глобальные переменные. :-)

Подробнее: Closures are not complicated

Теперь, иногда, вы хотите получить копию значения dia «s, как, когда была создана функция. В этих случаях вы обычно используете функцию построения и передаете 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. Ваш цикл:

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

Там нет элемента в dialogue[dialogue.length]. Последний элемент находится в dialogue[dialogue.length - 1]. Вы должны выйти из своей петли с < dialogue.length, а не <= dialogue.length. С < dialogue.length у вас все еще будет проблема: dia всегда будет последней записью (см. Выше), но, по крайней мере, она не будет неопределенной.

+1

Просто немного уточнить, почему вы видите " undefined ", если не закрывать 'dia', как TJ сделал выше, потому что до истечения таймаута ваша петля заканчивается, и вы застряли навсегда для доступа к диалогу [dialog.length + 1], который выходит за пределы. – MaxPRafferty

+0

@MaxPRafferty: На самом деле 'dia' не ссылается на диалог [dialog.length + 1]', он ссылается на 'dialog [dialog.length]'. Но это все равно вне пределов. –

+0

Спасибо за подробное объяснение! – emkay

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 так что он может взять индекс массива оттуда и взять правильный пункт

+0

Код без объяснений не очень полезен. –

+0

Извините, позвольте мне немного рассказать об этом решении, это решение просто передаст аргумент функции setTimeout, чтобы он мог взять индекс массива оттуда и взять правильный элемент – user1549458

+1

. Вы добавили бы это объяснение к своему ответу (отредактируйте свой ответ) ? –

Смежные вопросы