2013-12-10 8 views
2

У меня есть два блока кода, который я считаю, должен производить тот же результат:Javascript переменной копия сфера

1.

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

2.

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

Однако, как и большинство из вас можно ожидать, что первый журнал будет 9 десять раз, а второй - правильно от 0 до 9.

Второй использует замыкание для сохранения значения i. Я думаю, что первый должен сохранить значение, потому что:

  1. var j создать новую переменную j на каждой итерации.
  2. текущее значение i присваивается этому новому j на итерации.
  3. этот новый j затем привязан к функции setTimeout в той же итерации.
  4. следующая итерация свяжет новый j с функцией в этой итерации.

Но оказывается, что j привязан к последнему значению i для всей итерации.

Итак, в чем разница между созданием переменной с использованием аргумента функции и var?

Пожалуйста, укажите любую ошибку! Заранее спасибо !


Спасибо всем! Я не знал, что javascript имеет только функцию и глобальную область действия! Обвините других языков, которые научили меня делать это: P

+0

«var j» создает новую переменную j на каждой итерации »нет, это не так. – zzzzBov

ответ

3

Второй использует замыкание для сохранения значения I.

В самом деле, и результатов, которые вы видите являются результатом того, как работает замыкание. Обе функции, которые вы передаете в setTimeout, являются закрытиями.

Я думаю, что первым следует сохранить значение, а также, потому что:

вар J создать новую переменную J в каждой итерации.

Нет, это не так. Там только одинj в вашем первом примере. В JavaScript (пока) переменные только имеют функцию или глобальную область действия, никогда не блокируют область. То, что JavaScript на самом деле делает с первым примером выглядит гораздо больше, как это:

var i; 
var j; 

for(i=0;i<10;i+=1){ 
    j=i; 
    setTimeout(function(){ 
     console.log(j); 
    },100); 
} 

В результате закрытия имеет перенося ссылкой к тем одной j переменным, поэтому вы получите такое же значение, снова и снова.

Ваш второй пример - это немного анти-шаблон, потому что его трудно читать, и он бесполезно создает и отбрасывает функции в каждом цикле.Давайте сделаем это более очевидным, добавив некоторые переменные; этот код точно то же самое, как ваш второй пример, единственное изменение является промежуточным переменным я добавил:

for(var i=0;i<10;i+=1){ 
    var f1 = function(j){ 
     var f2 = function(){ 
      console.log(j); 
     }; 
     setTimeout(f2,100); 
    }; 
    f1(i); 
} 

Вместо:

for(var i=0;i<10;i+=1){ 
    setTimeout(makeHandler(i)); 
} 

function makeHandler(j){ 
    return function(){ 
     console.log(j); 
    }; 
} 

И легче читать и это позволяет избежать повторного -создание функции makeHandler на каждом цикле.

Подробнее изучить (в моем блоге):

+0

_enduring reference_, это хороший термин. Вы придумали это? – Halcyon

+0

@FritsvanCampen: Да, мне потребовалось некоторое время, чтобы придумать эту фразу, но я был доволен этим, когда я наконец это сделал. –

1

Это setTimeout, он откладывает выполнение до тех пор, пока браузер не будет доступен, или в этом случае 0,1 секунды спустя, и поскольку цикл блокирует браузер, timeOuts aren ' t выполняется до тех пор, пока цикл не завершится, и к этому времени переменная i равна последней, в которой она была установлена ​​в цикле, поскольку цикл завершен до того, как выполняется код в setTimeout.

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

for(var i=0;i<10;i+=1){ 
    var j=i;    // j is constantly updated and it's also hoisted 
    setTimeout(function(){ // and this runs last, when the browser is no longer 
     console.log(j);  // locked, so j is the last value it was set to 
    },100); 
} 

и

for(var i=0;i<10;i+=1){ 
    (function(j){ // creates a new scope with a local variable, the argument j 
     setTimeout(function(){ 
      console.log(j); 
     },100); 
    })(i); // passing i to the functions scope 
} 
+0

поэтому 'var j' не создает новый j на каждой итерации? – user3087656

+0

Нет, в цикле for нет специальной области, поэтому j является локальным для любой области, в которой находится цикл, и переменные поднимаются, поэтому вам нужно закрыть, чтобы поддерживать постоянное значение – adeneo

1

В своем первом примере кода j не связан значение iв этой итерации цикла. Это связано с тем, что вы не можете объявлять переменные в цикле. Циклы не имеют области видимости в JavaScript, только функции имеют область действия.

Первый образец код эквивалентен:

var i, j; // all variable declarations are hoisted to the top 
for (i = 0; i < 10; i += 1) { 
    j = i; 
    setTimeout(function() { 
     console.log(j); 
    }, 100); 
} 

Так как во втором примере кода j объявляется в анонимной-немедленным функции он связан со значением i в этой итерации из-Pass- по значению трюк. Это называется , закрывающее область или просто закрытие.

0

var j создает новую переменную j на каждой итерации.

Что, по вашему мнению, к сожалению, не соответствует Javascript. В Javascript, все объявления переменных перемещаются в верхнюю части ограждающей fucntion поэтому при вводе

function(){ 
    for(var i=1; i<10; i++){ 

Javascript на самом деле понимает, как

function(){ 
    var i; 
    for(i=1; i<20; i++){ 

Эта переменная Подъемный наиболее запутанная для петель с запорными но это также происходит и в других типах блоков. Например, в языках с регулярной лексической области видимости, следующий код выведет «1», так как внутренний «х» представляет собой отдельную переменную, которая существует только внутри этой ветви, если заявление:

var x = 1; 
if(true){ 
    var x = 2; 
} 
console.log(x); 

Однако в Javascript внутренняя переменная декларация получает водрузили так, его на самом деле, как если бы вы написали

var x; 
x = 1; 
if(true){ 
    x = 2; 
} 
console.log(x); 
0

Это работает, используя let вместо var (let не было вокруг еще в 2013 году, когда вопрос был задан):

for(let i=0;i<10;i+=1){ 
    let j=i; 
    setTimeout(function(){ 
     console.log(j); 
    },100); 
} 
Смежные вопросы