2016-11-25 3 views
1

У меня проблема с вызовом функции с параметром внутри функции setTimeout. В основном я пытаюсь создать небольшую онлайн-игру, где я создаю очередь команд, а затем выполняю их по одному (каждый из них занимает некоторое время, чтобы показать визуализацию).Переменная теряется при передаче в качестве параметра в setTimeout()

К сожалению, кажется, что я не могу передать какую-либо переменную в качестве параметра внутри setTimeout(). Хотя переменная существует, когда я вызываю функцию, она не существует позже, когда она выполняется. Функция не отслеживает переданное значение.

Есть ли какие-либо решения? Большое спасибо за любую помощь. Вот код, я использую:

function executeCommands() { 
    var commands = document.getElementsByClassName("cmdplace"); 
    var timeout = 0; 
    for (i = 0; i < commands.length; i++) { 
     console.log(commands[i].childNodes[0]); //variable exists 
     setTimeout(function() {go(commands[i].childNodes[0]);}, timeout+=400); //Uncaught TypeError: Cannot read property 'childNodes' of undefined 
     console.log(commands[i].childNodes[0]); //variable still exists 
    } 
} 

function go(command) { 
    //do somethig based on the passed command 
} 
+2

что пойти()? .. – baao

+0

Ах, извините. go() - это моя функция, которая делает что-то, основанное на переданной команде. –

+0

@PetrHofman, вы можете увидеть мой ответ ниже – Aruna

ответ

2

Когда ваши функции вызываются, i равно commands.length и commands[i] является undefined.
Они захватывают переменную i, а не ее значение.
Когда они выполняются, они получают из i фактическое значение, но до сих пор оно достигло commands.length (то есть условие, используемое для разрыва вашего цикла).

Вы можете сделать что-то вроде этого, чтобы работать вокруг него:

setTimeout(function(j) { 
    go(commands[j].childNodes[0]); 
}.bind(null, i), timeout+=400); 

Или это:

setTimeout((function(j) { 
    return function() { 
     go(commands[j].childNodes[0]); 
    }; 
})(i), timeout+=400); 

Заметим также, что, как вы определили, i является глобальной переменной.


Как уже упоминалось в комментариях @PMV, есть гораздо более простой способ в современной JavaScript (если это вариант для вас).
Просто используйте let заявление, как это следующим образом:

for (let i = 0; i < commands.length; i++) { 
    // do whatever you want here with i 
} 

Это будет гарантировать, что каждая итерация получает новую переменную с именем i и вы можете захватить его, как и в исходном коде.

+0

С ES6 гораздо проще: просто используйте оператор 'let':' for (let i = 0; i PMV

+0

@PMV Право. Я добавляю комментарий с упоминанием ответа. Спасибо. – skypjack

+0

@Petr hofman, пожалуйста, прочтите это https://community.risingstack.com/explaining-javascript-closure-scope-chain-examples/ –

0

Вам необходимо сделать отдельную копию каждого элемента. К моменту завершения цикла setTimeout цикл уже завершен.

var timeout = 0; 
 

 
function executeCommands() { 
 
    var commands = document.getElementsByClassName("cmdplace"); 
 
    
 
    for (i = 0; i < commands.length; i++) { 
 
      go(commands[i]); 
 
    } 
 
} 
 

 
function go(command) { 
 
    setTimeout(function() { 
 
    console.log(command); 
 
    }, timeout += 400); 
 
} 
 

 
executeCommands();
<ul> 
 
    <li class="cmdplace">A</li> 
 
    <li class="cmdplace">B</li> 
 
    <li class="cmdplace">C</li> 
 
    <li class="cmdplace">D</li> 
 
</ul>

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