Why is it that these two seemingly identical pieces of code behave differently in Javascript and 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()
The example in Lua prints 0 1 2 3 4 5 6 7 8 9
, but the example in Javascript prints 10 10 10 10 10 10 10 10 10 10
. Can anybody explain the difference between closures in Javascript and Lua that causes this to happen? I'm coming from a Javascript background, so please focus on the Lua side.
I attempted to explain this on my blog, but I'm not sure if my explanation is correct, so any clarification would be appreciated.
EDIT
Thanks everyone, now I understand. This slightly modified version of the Lua code prints 10,10,10,10,10,10,10,10,10,10 as expected
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()
It's as simple as the following:
Lua local
variables are scoped only to the nearest do-end
block, while JavaScript variables declared with var
are scoped to the nearest function boundaries. Closures get over this point by placing them in their own individual scope in a function, solving the problem of scope.
About your question regarding local i, j
being in the outer scope, the for statement in Lua creates the scope of the counter being used in the block scope, even if there is a variable declaration in the outer scope. The documentation says (reference):
The loop variable v is local to the loop; you cannot use its value after the for ends or is broken. If you need this value, assign it to another variable before breaking or exiting the loop.
This means that the var
initialisation is made local to the for loop scope, so placing local i, j
in the outer scope has no effect. This can be seen in an equivalent to Lua's for statement, given in the documentation:
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
However, JavaScript's for statement varies significantly (reference):
IterationStatement : for ( var VariableDeclarationListNoIn ; Expressionopt ; Expressionopt ) Statement
Because the declaration of the for loop is the same as any ordinary variable declaration, it's equivalent to placing it outside the loop, much different to how the Lua for loop works. The next version of ECMAScript (ES6) plans to introduce the let
keyword, which in a for loop will have a similar meaning to how Lua's for loop works:
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With