2012-04-10 3 views
14

Есть ли определенный источник при захвате переменной в Javascript, кроме стандартного (это боль, чтобы прочитать стандарт)?Понимание захвата переменной закрытием в Javascript/Node

В следующем коде i копируется значение:

for (var i = 0; i < 10; i++) 
{ 
    (function (i) 
    { 
     process.nextTick(function() 
     { 
      console.log(i) 
     }) 
    }) (i) 
} 

Так печатает 1..10. process.nextTick является аналогом setTimeout(f,0) в узле.

Но в следующем коде я, кажется, не будут скопированы:

for (var i = 0; i < 10; i++) 
{ 
     var j = i 
     process.nextTick(function() 
     { 
      console.log(j) 
     }) 
} 

Он печатает 9 10 раз. Зачем? Меня больше интересует ссылка/общая статья, чем объяснение этого конкретного случая захвата.

ответ

6

У меня нет удобной ссылки. Но в нижней строке: во-первых, вы явно передаете i анонимной функции, которая создает новую область. Вы не создаете новую область для i или j во втором. Кроме того, JavaScript всегда захватывает переменные, а не значения. Таким образом, вы тоже сможете изменить меня.

Ключевое слово JavaScript var имеет область действия, а не область блока. Таким образом, цикл for не создает область действия.

В качестве примечания, нестандартное ключевое слово let имеет локальный охват.

+0

Это не ясно, почему я не создаю новую область для J – nponeccop

+0

@nponeccop, JavaScript имеет функцию сферы. –

+0

Я ударился головой о стол. Не знал этого, предположил, что это C++ или Perl или Haskell :) Fascinating – nponeccop

4

Скопировано (или присвоено) в вашем втором примере, это всего лишь одна копия переменной j, и она будет иметь значение, которое у нее было в последней версии, которая будет равна 9 (последний оборот вашего цикла for). Для создания новой копии переменной для каждого оборота цикла for вам потребуется новое закрытие функции. Второй пример имеет только одну переменную, которая является общей для всех оборотов вашего цикла for, поэтому может иметь только одно значение.

Я не знаю окончательной записи по этой теме.

Переменные в javascript относятся к уровню функции. В javascript отсутствует область охвата. Таким образом, если вам нужна новая версия переменной для каждого оборота цикла for, вам нужно использовать новую функцию (создание закрытия функции), чтобы каждый раз записывать это новое значение через цикл for. Без закрытия функции одна переменная будет иметь только одно значение, которое будет общим для всех пользователей этой переменной.

Когда вы объявляете переменную, например, как ваш var j = i; на каком-то другом месте, чем в начале функции, JavaScript талей определение к верхней части функции и код становится эквивалентно следующему:

var j; 
for (var i = 0; i < 10; i++) 
{ 
     j = i; 
     process.nextTick(function() 
     { 
      console.log(j) 
     }) 
} 

Этот называется variable hoisting и это термин, который вы могли бы использовать Google, если хотите узнать больше об этом. Но дело в том, что существует только область функций, поэтому переменная, объявленная где угодно в функции, фактически объявляется один раз в верхней части функции и затем назначается в любом месте функции.

+0

Обновлен мой ответ с более подробной информацией о вашей ситуации. – jfriend00

4

В JavaScript функции заключают в себе переменные, которые были определены в области вне их собственных значений таким образом, что они имеют «живую» ссылку на переменную, а не снимок ее значения в какое-либо конкретное время.

Так что в вашем втором примере, вы создаете десять анонимных функций (в process.nextTick(function(){...})), которые ограждают переменная ji, которые всегда имеют такое же значение, когда анонимная функция создается). Каждая из этих функций использует значение j за один раз после внешний цикл for работает полностью, поэтому j=i=10 в то время, когда вызывается каждая из функций. То есть, сначала ваш цикл for работает полностью, тогда ваши анонимные функции запускаются и используют значение j, которое уже установлено в 10!

В вашем первом примере ситуация немного отличается. Обернув вызов process.nextTick(...) в его собственную анонимную функцию и связав значение i с функцией-локальной областью, вызвав функцию обертки (и, кстати, затеняя старую переменную i в параметр функции i), вы фиксируете значение от переменной i при этом момент вместо сохранения прилагается ссылка на номер i, значение которого изменяется в приложении внутренних анонимных функций.

Чтобы прояснить свой первый пример, попробуйте изменить функцию анонимной обертки, чтобы использовать аргумент с именем x ((function (x) { process.nextTick(...); })(i)). Здесь мы ясно видим, что x принимает значение в i в момент вызова анонимной функции, чтобы получить каждое из значений в for-loop (1..10).

+0

Каково наилучшее решение для захвата значения вместо сохранения прилагаемой ссылки? –