2014-12-06 5 views
0

Я думал, что я хорошо разбираюсь в Javascript, но, видимо, я этого не делаю. Следующий код не работает. Как я могу распечатать все цифры от 0 до 9 в консоли после 1 секунды задержки? В настоящее время он просто печатает «неопределенные» десять раз.Замыкания с обратными вызовами Javascript

ПРИМЕЧАНИЕ: Я не ищу более простой тренировки для распечатки чисел после задержки. Этот вопрос касается понимания закрытия.

<script> 
for(var i=0;i<10;i++){ 
    setTimeout(function(i){console.log(i)}, 1000) 
} 
</script> 

ответ

2

Колпачки имеют важное значение в JavaScript, но важно понять, что именно вы closing над. Чтобы переработать ваш текущий код (вид).

function(){ 
    var i; 
    for(i=0;i<10;i++){ 
     setTimeout(function(){ 
      console.log(i); 
     }, 1000); 
    } 
} 

В этом примере, код в основном закрытие над var i;, что означает, что, когда работает таймер, он будет считывать значение var i; и печать это. В этом случае, как вы видели, когда таймер запускается, цикл завершается, и значение равно 10.

Что вы хотите сделать, это создать новую область функций, которая фиксирует значение i в определенное время.

function(){ 
    var i; 
    for(i=0;i<10;i++){ 
     (function(iInner){ 
      setTimeout(function(){ 
       console.log(iInner); 
      }, 1000); 
     })(i); 
    } 
} 

Этот пример создаст новую анонимную функцию, а затем вызвать его сразу в петлю, и передать текущее значение i в него, так что, когда таймер читает iInner, он будет считывать значение, которое было передается в функцию, а не значение от var i;. Вы также можете просто позвонить iInneri, если хотите, но я использовал два разных имени для ясности.

Есть также помощники, которые вы можете использовать, например .bind, которые по существу автоматически создадут для вас новую анонимную функцию и передадут аргументы, подобные этому.

function(){ 
    var i; 
    for(i=0;i<10;i++){ 
     setTimeout(function(iInner){ 
      console.log(iInner); 
     }.bind(null, i), 1000); 
    } 
} 

<func>.bind примет значение i и возвращает новую функцию, которая проходит эту арг через к <func> при вызове, и избежать того, чтобы создать еще один слой вложенности.

0

Вы определили функцию обратного вызова принять параметр i, который маскирует closurable i объявленных в течение цикла. Поэтому изменить его на:

<script> 
for(var i=0;i<10;i++){ 
    setTimeout(function(){console.log(i)}, 1000) 
} 
</script> 

Edit: К сожалению, я не смотрел на общее намерение вашего вопроса. Что касается способа закрытия замыканий, объект функции фиксирует ссылку на любую переменную в охватывающей области, на которую ссылается внутри тела функции, и которая не связывается с какой-либо локальной переменной (параметр или явным объявлением var) внутри тела , Вот почему ваша первоначальная попытка не сработала; i связывался с локальным параметром.

Функции объект делает не захвата значения, что переменная closured обладала в то время функция была определена; он фиксирует ссылку . Таким образом, вы не можете перебирать одну переменную (я говорю о объявлении var i в цикле for) с намерением закрыть значение для каждого последующего определения функции. Значения не закрываются; переменные.

Но, вы можете эффективно закрывать значение путем закрытия на временном локальном, которое имеет значение, которое вы хотите захватить, в то время, когда вы определяете функцию. Это требует создания новой функции сферы и определения функции closuring внутри этой сферы, closuring вокруг локального (параметр функции), который имеет значение, которое вы хотите:

<script> 
for (var i_outer = 0; i_outer < 10; ++i_outer) 
    setTimeout((function(i_inner) { return function() { console.log(i_inner); }; })(i_outer), 1000); 
</script> 
+0

Нет! Это просто печатает '10' десять раз. Вот почему нам нужны замыкания. –

0

Это, как вы это делаете. Вы создаете IIFE (Expression Exited Function Expression), который возвращает функцию, которая выводит на консоль. Помните, что значение i, должно быть проглочено внутренней функцией, передавая его в качестве аргумента.

setTimeout ожидает «функцию» в качестве первого аргумента, и именно по этой причине мы возвращаем функцию из IIFE.

for(var i=0;i<10;i++){ 

    setTimeout((function(i) { 
     return function() { 
      console.log(i); 
     } 
    })(i), 1000) 
} 

Благодаря

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