Javascript和Lua之间的闭包差异

Fli*_*sey 13 javascript lua closures scope

为什么这两个看似相同的代码片段在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()
Run Code Online (Sandbox Code Playgroud)

使用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()
Run Code Online (Sandbox Code Playgroud)

Lua中的示例打印0 1 2 3 4 5 6 7 8 9,但Javascript中的示例打印10 10 10 10 10 10 10 10 10 10.任何人都可以解释Javascript和Lua中导致这种情况发生的闭包之间的区别吗?我来自Javascript背景,所以请关注Lua方面.

我试图在我的博客上解释这一点,但我不确定我的解释是否正确,所以任何澄清都将不胜感激.

编辑

谢谢大家,现在我明白了.这个稍微修改过的版本的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()
Run Code Online (Sandbox Code Playgroud)

Qan*_*avy 7

它简单如下:

Lua local变量的范围仅限于最近的do-end块,而声明的JavaScript变量var的范围限定为最近的函数边界.闭包通过将它们放在函数中的各自范围内来解决问题,解决了范围问题.

关于您local i, j在外部范围内的问题,Lua中的for语句创建了在块作用域中使用的计数器的范围,即使外部作用域中存在变量声明也是如此.文件说(参考):

循环变量v是循环的局部变量; 你不能在结束后使用它的价值或被打破.如果需要此值,请在断开或退出循环之前将其分配给另一个变量.

这意味着var初始化是针对for循环范围进行的,因此放置local i, j在外部范围内无效.这可以在Lua的for语句中看到,在文档中给出:

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
Run Code Online (Sandbox Code Playgroud)

但是,JavaScript的声明差别很大(参考):

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

因为for循环的声明与任何普通变量声明相同,所以它等同于将它放在循环之外,与Lua for循环的工作方式大不相同.ECMAScript的下一个版本(ES6)计划引入let关键字,在for循环中,它与Lua for for循环的工作方式具有相似的含义:

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
Run Code Online (Sandbox Code Playgroud)

  • @FlightOdyssey:这不适用于Lua案例.Lua for循环总是声明一个新变量,因此循环中的变量是每次迭代的新变量.你声明的`local i`最终会被遮蔽并被忽略(如果删除它,结果应该是相同的). (2认同)