13

これら 2 つの一見同一のコードが、Javascript と 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()
4

1 に答える 1

7

次のように簡単です。

Lualocal変数は最も近いdo-endブロックのみにスコープが設定されますが、で宣言された JavaScript 変数はvar最も近い関数境界にスコープが設定されます。クロージャーは、関数内の独自のスコープに配置することでこの点を克服し、スコープの問題を解決します。

外側のスコープについてのご質問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 ) ステートメント

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-08T00:06:21.470 に答える