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代码示例中,输出了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()
点赞
用户2074608
用户2074608

以下是翻译:

这很简单:

Lua 的 local 变量的作用域仅限于最近的 do-end 块,而使用 var 声明的 JavaScript 变量的作用域限于最近的函数边界。通过将闭包放置在函数中的各自的范围中,可以解决作用域问题。

关于您关于 local i, j 在外部作用域的问题,Lua 中的 for 循环语句创建了在块作用域中使用的计数器的作用域,即使在外部作用域中有变量声明也是如此。文档说明(参考)如下:

循环变量 v 局限于循环;在 for 结束或被杀死后,您无法使用它的值。如果需要该值,请在退出循环之前将其分配给另一个变量。

这意味着 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

但是,JavaScript 的 for 语句有很大的不同(参考):

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

由于 for 循环的声明与任何普通变量声明相同,因此它相当于将其放置在循环外部,这与 Lua 的 for 循环工作方式有很大的区别。 ECMAScript 的下一个版本(ES6)计划引入 let 关键字,该关键字在 for 循环中将具有与 Lua 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
2013-10-08 00:06:21