2013-10-07 5 views
12

Почему эти два, казалось бы, идентичных фрагмента кода ведут себя по-разному в Javascript и Lua?Разница в закрытии между Javascript и Lua

Lua:

function main() 
    local printFunctions={} 
    local i,j 
    for i=1,10 do 
     local printi = function() 
      print(i) 
     end 
     printFunctions[i]=printi 
    end 
    for j=1,10 do 
     printFunctions[j]() 
    end 
end 
main() 

Javascript:

function main() 
{ 
    var printFunctions=[] 
    var i,j; 
    for(i=0;i<10;i++) 
    { 
     var printi = function() 
     { 
      console.log(i); 
     } 
     printFunctions[i]=printi; 
    } 
    for(j=0;j<10;j++) 
    { 
     printFunctions[j](); 
    } 
} 
main() 

Пример в Lua печатает 0 1 2 3 4 5 6 7 8 9, но пример в Javascript распечатывает . Может ли кто-нибудь объяснить разницу между закрытиями в Javascript и Lua, которые заставляют это произойти? Я исхожу из фона Javascript, поэтому, пожалуйста, сосредоточьтесь на стороне Lua.

Я попытался объяснить это на my blog, но я не уверен, что мои объяснения верны, поэтому любые разъяснения будут оценены.

EDIT

Спасибо всем, теперь я понимаю. Это слегка измененный вариант кода отпечатков Lua 10,10,10,10,10,10,10,10,10,10, как ожидается,

function main() 
    local printFunctions={} 
    local i,j,k 
    for i=1,10 do 
     k=i 
     local printi = function() 
      print(k) 
     end 
     printFunctions[i]=printi 
    end 
    for j=1,10 do 
     printFunctions[j]() 
    end 
end 

main() 

ответ

7

Это так просто, как:

Lua local переменные ограничены только до ближайшего блока do-end, а переменные JavaScript, объявленные с помощью var, имеют область действия до ближайших границ функции. Закрытие преодолевает этот момент, помещая их в свою собственную область действия в функцию, решая проблему области.

О вашем вопросе относительно local i, j, находящемся во внешней сфере, оператор for в Lua создает область действия счетчика, используемого в области блока, даже если во внешней области есть объявление переменной. В документации указано (reference):

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

Это означает, что var инициализация выполнена местная до объема для петли, так что размещение local i, j во внешнем объеме не имеет никакого эффекта. Это можно увидеть в эквивалентном Lua для поддержки оператора, в документации:

do 
    local var, limit, step = tonumber(e1), tonumber(e2), tonumber(e3) 
    if not (var and limit and step) then error() end 
    while (step > 0 and var <= limit) or (step <= 0 and var >= limit) do 
     local v = var 
     block 
     var = var + step 
    end 
end 

Однако, JavaScript для заявления существенно различается (reference):

IterationStatement : for (var VariableDeclarationListNoIn ; Expressionopt ; Expressionopt) Statement

Поскольку декларация для цикла является так же, как и любое обычное объявление переменной, это эквивалентно размещению его вне цикла, что сильно отличается от того, как работает цикл Lua for. Следующая версия ECMAScript (ES6) планирует ввести let ключевое слово, которое в цикле будет иметь такой же смысл, как работает Lua для цикла:

for (let i = 0; i < 10; ++i) setTimeout(function() { console.log(i); }, 9); // 0,1,2,3,4,5,6,7,8,9 
for (var i = 0; i < 10; ++i) setTimeout(function() { console.log(i); }, 9); // 10,10,10,10,10,10,10,10,10,10 
+0

Но в обоих случаях '' i' и j' были объявленные в самой внешней области (в функции 'main'), поэтому они должны иметь одинаковую область действия на обоих языках. –

+2

@FlightOdyssey: это неверно для случая Lua. Lua for loops всегда объявляют новую переменную, поэтому переменная в цикле является новой переменной для каждой итерации. Заявление «local i», которое вы сделали, будет затенено и проигнорировано (если вы удалите его, результат будет таким же). – hugomg

+0

@ missingno: О, я вижу - спасибо! –

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