27

標準以外にJavascriptの変数キャプチャに関する明確な情報源はありますか(標準を読むのは面倒です)?

次のコードiでは、値によってコピーされます。

for (var i = 0; i < 10; i++)
{
    (function (i)
    {
        process.nextTick(function ()
        {
            console.log(i)
        })
    }) (i)
}

したがって、1..10を出力します。inノードprocess.nextTickのアナログです。setTimeout(f,0)

しかし、次のコードでは、私はコピーされていないようです:

for (var i = 0; i < 10; i++)
{
        var j = i
        process.nextTick(function ()
        {
            console.log(j)
        })
}

910回印刷します。なんで?この具体的なキャプチャのケースを説明するよりも、リファレンス/一般的な記事に興味があります。

4

4 に答える 4

18

便利なリファレンスはありません。しかし、肝心なのは次のとおりです。最初にi、匿名関数に明示的に渡して、新しいスコープを作成します。iどちらかまたは2番目のスコープに新しいスコープを作成していませんj。また、JavaScriptは値ではなく、常に変数をキャプチャします。だからあなたも私を変更することができるでしょう。

JavaScriptvarキーワードには、ブロックスコープではなく関数スコープがあります。したがって、forループはスコープを作成しません。

注意として、非標準letキーワードにはローカルスコープがあります。

于 2012-04-10T19:50:23.990 に答える
5

2番目の例ではコピー(または割り当て)されています。変数のコピーが1つしかないだけでj、最後に持っていた値が9(forループの最後の回転)になります。forループの各回転に対して変数の新しいコピーを作成するには、新しい関数クロージャが必要です。2番目の例には、 forループのすべての回転数に共通する変数が1つしかないため、値を1つだけ持つことができます。

私はこのトピックに関する決定的な記事を知りません。

javascriptの変数は、関数レベルにスコープされています。javascriptにはブロックスコープはありません。そのため、forループの各回転に対して新しいバージョンの変数が必要な場合は、新しい関数を使用して(関数クロージャを作成)、ループを通過するたびにその新しい値をキャプチャする必要がありforます。関数クロージャがない場合、1つの変数には、その変数のすべてのユーザーに共通の1つの値が含まれます。

関数の先頭以外の場所でyourなどの変数を宣言するとvar j = i;、javascriptは定義を関数の先頭に引き上げ、コードは次のようになります。

var j;
for (var i = 0; i < 10; i++)
{
        j = i;
        process.nextTick(function ()
        {
            console.log(j)
        })
}

これは呼ばれvariable hoisting、それについてもっと読みたい場合はグーグルでできる用語です。ただし、要点は、関数スコープしかないため、関数内のどこかで宣言された変数は、実際には関数の先頭で一度宣言されてから、関数内のどこかに割り当てられるということです。

于 2012-04-10T19:50:34.523 に答える
5

JavaScriptでは、関数は、特定の時点での値のスナップショットではなく、変数への「生きた」参照を持つように、自身の外部のスコープで定義された変数を囲みます。

したがって、2番目の例では、変数を囲むprocess.nextTick(function(){...})10個の無名関数(in )を作成します(および、無名関数の作成時に常に同じ値を持ちます)。これらの各関数は、外側のforループが完全に実行された、各関数が呼び出された時点での値を使用します。つまり、最初にforループが完全に実行され、次に無名関数が実行され、すでに10に設定されている値を使用します。jijj=i=10j

最初の例では、状況が少し異なります。呼び出しをprocess.nextTick(...)それ自体の無名関数でiラップし、ラッパー関数を呼び出しての値を関数ローカルスコープにバインドする(そして偶然に古い変数を関数パラメーターにシャドウイングする)ことで、その時点での変数の値をキャプチャします、内部の無名関数のエンクロージャーで値が変更される囲まれた参照を保持する代わりに。iiii

x最初の例をいくらか明確にするために、 ( )という名前の引数を使用するように匿名ラッパー関数を変更してみてください(function (x) { process.nextTick(...); })(i)。ここでは、匿名関数が呼び出された時点でx値を取り込んでいることがはっきりとわかります。これにより、forループ(1..10)の各値が取得されます。i

于 2012-04-10T20:01:53.730 に答える
0

最初の例では、i'は同じではありません。

for (var i = 0; i < 10; i++) {
    (function (i) { //  capture i as the new i in this closure scope.
        process.nextTick(function () {
            console.log(i) //  reference new i (captured).
        })
    })(i)
}

クロージャ/バリューキャプチャ-ロゼッタコード

于 2021-02-23T02:13:21.363 に答える