ループ内のJavaScriptクロージャ–簡単な実用例

Apr 15 2009

var funcs = [];
// let's create 3 functions
for (var i = 0; i < 3; i++) {
  // and store them in funcs
  funcs[i] = function() {
    // each should log its value.
    console.log("My value: " + i);
  };
}
for (var j = 0; j < 3; j++) {
  // and now let's run each one to see
  funcs[j]();
}

これを出力します:

私の価値:3
私の価値:3
私の価値:3

私はそれを出力したいのに対し:

私の価値:0
私の価値:1
私の価値:2


関数の実行の遅延がイベントリスナーの使用によって引き起こされた場合にも、同じ問題が発生します。

var buttons = document.getElementsByTagName("button");
// let's create 3 functions
for (var i = 0; i < buttons.length; i++) {
  // as event listeners
  buttons[i].addEventListener("click", function() {
    // each should log its value.
    console.log("My value: " + i);
  });
}
<button>0</button>
<br />
<button>1</button>
<br />
<button>2</button>

…または非同期コード、たとえばPromisesの使用:

// Some async wait function
const wait = (ms) => new Promise((resolve, reject) => setTimeout(resolve, ms));

for (var i = 0; i < 3; i++) {
  // Log `i` as soon as each promise resolves.
  wait(i * 100).then(() => console.log(i));
}

それはまたfor infor ofループで明らかです:

const arr = [1,2,3];
const fns = [];

for(var i in arr){
  fns.push(() => console.log(`index: ${i}`)); } for(var v of arr){ fns.push(() => console.log(`value: ${v}`));
}

for(var f of fns){
  f();
}

この基本的な問題の解決策は何ですか?

回答

2214 harto Apr 15 2009 at 13:18

問題は、i各無名関数内の変数が、関数外の同じ変数にバインドされていることです。

ES6ソリューション: let

ECMAScriptの6(ES6)新発売letconst違ったよりもスコープされるキーワードvarベースの変数。たとえば、letベースのインデックスを持つループでは、ループの各反復にiループスコープを持つ新しい変数が含まれるため、コードは期待どおりに機能します。多くのリソースがありますが、優れた情報源として2alityのブロックスコープ投稿をお勧めします。

for (let i = 0; i < 3; i++) {
  funcs[i] = function() {
    console.log("My value: " + i);
  };
}

ただし、Edge 14より前のIE9-IE11とEdgeはサポートしますletが、上記は間違っていることに注意してください(i毎回新しいものを作成しないため、上記のすべての関数は、使用した場合と同じように3をログに記録しますvar)。エッジ14はついにそれを正しくします。


ES5.1ソリューション:forEach

Array.prototype.forEach関数が比較的広く利用可能になっているため(2015年)、主に値の配列に対する反復を伴う状況で.forEach()は、反復ごとに明確なクロージャーを取得するためのクリーンで自然な方法が提供されることに注意してください。つまり、値(DOM参照、オブジェクトなど)を含むある種の配列があり、各要素に固有のコールバックを設定する際に問題が発生すると仮定すると、次のように実行できます。

var someArray = [ /* whatever */ ];
// ...
someArray.forEach(function(arrayElement) {
  // ... code code code for this one element
  someAsynchronousFunction(arrayElement, function() {
    arrayElement.doSomething();
  });
});

アイデアは、.forEachループで使用されるコールバック関数の各呼び出しが独自のクロージャになるということです。そのハンドラーに渡されるパラメーターは、反復のその特定のステップに固有の配列要素です。非同期コールバックで使用される場合、反復の他のステップで確立された他のコールバックのいずれとも衝突しません。

jQueryで作業している場合、この$.each()関数は同様の機能を提供します。


古典的な解決策:クロージャ

実行したいのは、各関数内の変数を、関数外の個別の不変の値にバインドすることです。

var funcs = [];

function createfunc(i) {
  return function() {
    console.log("My value: " + i);
  };
}

for (var i = 0; i < 3; i++) {
  funcs[i] = createfunc(i);
}

for (var j = 0; j < 3; j++) {
  // and now let's run each one to see
  funcs[j]();
}

JavaScriptにはブロックスコープがなく、関数スコープのみであるため、関数の作成を新しい関数でラップすることにより、「i」の値が意図したとおりに維持されるようにします。

390 Bjorn Apr 15 2009 at 13:10

試してみてください:

var funcs = [];
    
for (var i = 0; i < 3; i++) {
    funcs[i] = (function(index) {
        return function() {
            console.log("My value: " + index);
        };
    }(i));
}

for (var j = 0; j < 3; j++) {
    funcs[j]();
}

編集(2014):

個人的には、@ Austの使用に関する最近の回答.bindが、この種のことを今行うための最良の方法だと思います。's_.partialをいじる必要がない、またはいじりたくない場合は、lo-dash / underscoreもあります。bindthisArg

357 Aust Oct 11 2013 at 23:41

まだ言及されていない別の方法は、 Function.prototype.bind

var funcs = {};
for (var i = 0; i < 3; i++) {
  funcs[i] = function(x) {
    console.log('My value: ' + x);
  }.bind(this, i);
}
for (var j = 0; j < 3; j++) {
  funcs[j]();
}

更新

@squintと@mekdevで指摘されているように、最初にループの外側で関数を作成し、次にループ内で結果をバインドすることで、パフォーマンスが向上します。

function log(x) {
  console.log('My value: ' + x);
}

var funcs = [];

for (var i = 0; i < 3; i++) {
  funcs[i] = log.bind(this, i);
}

for (var j = 0; j < 3; j++) {
  funcs[j]();
}

275 neurosnap Oct 12 2013 at 01:23

即時呼び出し関数式を使用して、インデックス変数を囲む最も簡単で読みやすい方法:

for (var i = 0; i < 3; i++) {

    (function(index) {

        console.log('iterator: ' + index);
        //now you can also loop an ajax call here 
        //without losing track of the iterator value:   $.ajax({});
    
    })(i);

}

これにより、イテレータiindex。として定義されている無名関数に送信されます。これによりクロージャが作成され、変数iは後でIIFE内の非同期機能で使用するために保存されます。

170 woojoo666 Apr 10 2015 at 16:57

パーティーに少し遅れましたが、今日この問題を調査していて、回答の多くがJavascriptがスコープを処理する方法に完全に対応していないことに気付きました。これは、本質的にはこれに要約されます。

他の多くの人が述べたように、問題は、内部関数が同じi変数を参照していることです。では、反復ごとに新しいローカル変数を作成し、代わりに内部関数参照を使用しないのはなぜですか?

//overwrite console.log() so you can see the console output
console.log = function(msg) {document.body.innerHTML += '<p>' + msg + '</p>';};

var funcs = {};
for (var i = 0; i < 3; i++) {
    var ilocal = i; //create a new local variable
    funcs[i] = function() {
        console.log("My value: " + ilocal); //each should reference its own local variable
    };
}
for (var j = 0; j < 3; j++) {
    funcs[j]();
}

以前と同様に、各内部関数はに割り当てられた最後の値を出力していましたがi、今では各内部関数はに割り当てられた最後の値を出力するだけilocalです。しかし、各反復に独自のものがあるべきではありませんilocalか?

結局のところ、それが問題です。各反復は同じスコープを共有しているため、最初の反復以降のすべての反復は単に上書きされますilocal。MDNから:

重要:JavaScriptにはブロックスコープがありません。ブロックで導入された変数は、含まれている関数またはスクリプトにスコープされ、それらを設定した効果はブロック自体を超えて持続します。言い換えると、ブロックステートメントはスコープを導入しません。「スタンドアロン」ブロックは有効な構文ですが、JavaScriptでスタンドアロンブロックを使用することは望ましくありません。CやJavaでそのようなブロックのようなことをすると思うと、思ったとおりに機能しないからです。

強調のために繰り返し:

JavaScriptにはブロックスコープがありません。ブロックで導入された変数は、含まれている関数またはスクリプトにスコープされます

ilocalこれは、各反復で宣言する前に確認することで確認できます。

//overwrite console.log() so you can see the console output
console.log = function(msg) {document.body.innerHTML += '<p>' + msg + '</p>';};

var funcs = {};
for (var i = 0; i < 3; i++) {
  console.log(ilocal);
  var ilocal = i;
}

これがまさにこのバグがとてもトリッキーな理由です。変数を再宣言している場合でも、Javascriptはエラーをスローせず、JSLintは警告もスローしません。これを解決する最善の方法がクロージャを利用することであるのもこのためです。これは基本的に、Javascriptでは、内部スコープが外部スコープを「囲む」ため、内部関数が外部変数にアクセスできるという考えです。

これは、外部関数が戻った場合でも、内部関数が外部変数を「保持」し、それらを存続させることも意味します。これを利用するために、ラッパー関数を作成して呼び出し、純粋に新しいスコープを作成し、新しいスコープで宣言ilocalし、ilocal以下を使用する内部関数を返します(以下で詳しく説明します)。

//overwrite console.log() so you can see the console output
console.log = function(msg) {document.body.innerHTML += '<p>' + msg + '</p>';};

var funcs = {};
for (var i = 0; i < 3; i++) {
    funcs[i] = (function() { //create a new scope using a wrapper function
        var ilocal = i; //capture i into a local var
        return function() { //return the inner function
            console.log("My value: " + ilocal);
        };
    })(); //remember to run the wrapper function
}
for (var j = 0; j < 3; j++) {
    funcs[j]();
}

ラッパー関数内に内部関数を作成すると、内部関数に、それだけがアクセスできるプライベート環境である「クロージャ」が与えられます。したがって、ラッパー関数を呼び出すたびに、独自の個別の環境で新しい内部関数を作成し、ilocal変数が衝突して互いに上書きしないようにします。いくつかのマイナーな最適化により、他の多くのSOユーザーが提供した最終的な答えが得られます。

//overwrite console.log() so you can see the console output
console.log = function(msg) {document.body.innerHTML += '<p>' + msg + '</p>';};

var funcs = {};
for (var i = 0; i < 3; i++) {
    funcs[i] = wrapper(i);
}
for (var j = 0; j < 3; j++) {
    funcs[j]();
}
//creates a separate environment for the inner function
function wrapper(ilocal) {
    return function() { //return the inner function
        console.log("My value: " + ilocal);
    };
}

更新

ES6が主流になったので、新しいletキーワードを使用してブロックスコープの変数を作成できるようになりました。

//overwrite console.log() so you can see the console output
console.log = function(msg) {document.body.innerHTML += '<p>' + msg + '</p>';};

var funcs = {};
for (let i = 0; i < 3; i++) { // use "let" to declare "i"
    funcs[i] = function() {
        console.log("My value: " + i); //each should reference its own local variable
    };
}
for (var j = 0; j < 3; j++) { // we can use "var" here without issue
    funcs[j]();
}

今ではとても簡単です!詳細については、私の情報の基になっているこの回答を参照してください。

157 BenMcCormick May 21 2013 at 10:04

ES6が広くサポートされるようになったため、この質問に対する最良の答えが変わりました。ES6は、この正確な状況に対応するキーワードletconstキーワードを提供します。クロージャをいじくり回す代わりに、次のletようにループスコープ変数を設定するために使用できます。

var funcs = [];

for (let i = 0; i < 3; i++) {          
    funcs[i] = function() {            
      console.log("My value: " + i); 
    };
}

val次に、ループのその特定のターンに固有のオブジェクトを指し、追加のクロージャー表記なしで正しい値を返します。これは明らかにこの問題を大幅に単純化します。

constlet、最初の割り当て後に変数名を新しい参照にリバウンドできないという追加の制限がある点と似ています。

最新バージョンのブラウザを対象とするブラウザのサポートがここにあります。const/letは現在、最新のFirefox、Safari、Edge、Chromeでサポートされています。Nodeでもサポートされており、Babelなどのビルドツールを利用してどこでも使用できます。ここで実際の例を見ることができます:http://jsfiddle.net/ben336/rbU4t/2/

ここのドキュメント:

  • const
  • しましょう

ただし、Edge 14より前のIE9-IE11とEdgeはサポートしますletが、上記は間違っていることに注意してください(i毎回新しいものを作成しないため、上記のすべての関数は、使用した場合と同じように3をログに記録しますvar)。エッジ14はついにそれを正しくします。

91 DarrenClark Apr 15 2009 at 13:48

別の言い方をすれば、i関数内は、関数の作成時ではなく、関数の実行時にバインドされるということです。

クロージャを作成するときiは、外部スコープで定義された変数への参照であり、クロージャを作成したときのようにそのコピーではありません。実行時に評価されます。

他のほとんどの回答は、値を変更しない別の変数を作成することで回避する方法を提供します。

わかりやすくするために説明を追加したいと思いました。解決策として、個人的には、ここでの回答からそれを行う最も自明な方法であるHarto'sを使用します。投稿されたコードはどれでも機能しますが、新しい変数を宣言する理由(Freddyと1800)や奇妙な埋め込みクロージャ構文(apphacker)を説明するコメントの山を書く必要はなく、クロージャファクトリを選択します。

74 eglasius Apr 15 2009 at 13:25

あなたが理解する必要があるのは、javascriptの変数のスコープが関数に基づいているということです。これは、ブロックスコープがあるc#と言うのとは重要な違いであり、変数をfor内の変数にコピーするだけで機能します。

変数に関数スコープがあるので、apphackerの答えのように関数を返すことを評価する関数でそれをラップすると、トリックが実行されます。

varの代わりにletキーワードもあり、ブロックスコープルールを使用できます。その場合、for内で変数を定義するとうまくいきます。とはいえ、letキーワードは互換性があるため、実用的なソリューションではありません。

var funcs = {};

for (var i = 0; i < 3; i++) {
  let index = i; //add this
  funcs[i] = function() {
    console.log("My value: " + index); //change to the copy
  };
}

for (var j = 0; j < 3; j++) {
  funcs[j]();
}

63 Boann Aug 07 2012 at 15:45

ビョルン(apphacker)に似た、この手法の別のバリエーションを次に示します。これにより、変数値をパラメーターとして渡すのではなく、関数内で割り当てることができます。これは、より明確な場合があります。

var funcs = [];
for (var i = 0; i < 3; i++) {
    funcs[i] = (function() {
        var index = i;
        return function() {
            console.log("My value: " + index);
        }
    })();
}

どの手法を使用する場合でも、index変数は一種の静的変数になり、内部関数の返されたコピーにバインドされることに注意してください。つまり、その値の変更は呼び出し間で保持されます。とても便利です。

58 Noname Apr 20 2013 at 16:59

これは、JavaScriptでクロージャを使用する際の一般的な間違いについて説明しています。

関数は新しい環境を定義します

考えてみましょう:

function makeCounter()
{
  var obj = {counter: 0};
  return {
    inc: function(){obj.counter ++;},
    get: function(){return obj.counter;}
  };
}

counter1 = makeCounter();
counter2 = makeCounter();

counter1.inc();

alert(counter1.get()); // returns 1
alert(counter2.get()); // returns 0

makeCounter呼び出されるたび{counter: 0}に、新しいオブジェクトが作成されます。また、obj新しいオブジェクトを参照するために、の新しいコピーも作成されます。したがって、counter1counter2は互いに独立しています。

ループ内のクロージャ

ループでクロージャを使用するのは注意が必要です。

考えてみましょう:

var counters = [];

function makeCounters(num)
{
  for (var i = 0; i < num; i++)
  {
    var obj = {counter: 0};
    counters[i] = {
      inc: function(){obj.counter++;},
      get: function(){return obj.counter;}
    }; 
  }
}

makeCounters(2);

counters[0].inc();

alert(counters[0].get()); // returns 1
alert(counters[1].get()); // returns 1

counters[0]counters[1]は独立していないことに注意してください。実際、それらは同じように動作しobjます!

これはobj、おそらくパフォーマンス上の理由から、ループのすべての反復で共有されるコピーが1つしかないためです。にもかかわらず、{counter: 0}各反復で新しいオブジェクトを作成し、同じコピーobj意志は、単に最新のオブジェクトを参照して更新されます。

解決策は、別のヘルパー関数を使用することです。

function makeHelper(obj)
{
  return {
    inc: function(){obj.counter++;},
    get: function(){return obj.counter;}
  }; 
}

function makeCounters(num)
{
  for (var i = 0; i < num; i++)
  {
    var obj = {counter: 0};
    counters[i] = makeHelper(obj);
  }
}

これが機能するのは、関数スコープ内のローカル変数と関数引数変数に、入力時に新しいコピーが割り当てられるためです。

51 KemalDağ Jun 25 2013 at 21:21

最も簡単な解決策は、

使用する代わりに:

var funcs = [];
for(var i =0; i<3; i++){
    funcs[i] = function(){
        alert(i);
    }
}

for(var j =0; j<3; j++){
    funcs[j]();
}

これは「2」を3回警告します。これは、forループで作成された無名関数が同じクロージャを共有し、そのクロージャではの値がi同じであるためです。これを使用して、共有クロージャを防止します。

var funcs = [];
for(var new_i =0; new_i<3; new_i++){
    (function(i){
        funcs[i] = function(){
            alert(i);
        }
    })(new_i);
}

for(var j =0; j<3; j++){
    funcs[j]();
}

この背後にある考え方は、forループの本体全体をIIFE(Immediately-Invoked Function Expression)でカプセル化new_iし、パラメーターとして渡し、それをとしてキャプチャすることiです。匿名関数はすぐに実行されるため、匿名関数i内で定義された関数ごとに値が異なります。

この解決策は、この問題に悩まされている元のコードへの最小限の変更を必要とするため、このような問題に適合するようです。実際、これは仕様によるものであり、まったく問題にはならないはずです。

33 Daryl May 03 2014 at 10:42

を使用する簡単なソリューションはforEach次のとおりです(IE9に戻ります)。

var funcs = [];
[0,1,2].forEach(function(i) {          // let's create 3 functions
    funcs[i] = function() {            // and store them in funcs
        console.log("My value: " + i); // each should log its value.
    };
})
for (var j = 0; j < 3; j++) {
    funcs[j]();                        // and now let's run each one to see
}

プリント:

My value: 0
My value: 1
My value: 2
32 yilmazburk Sep 19 2013 at 21:20

この短いものを試してください

  • 配列なし

  • 余分なforループはありません


for (var i = 0; i < 3; i++) {
    createfunc(i)();
}

function createfunc(i) {
    return function(){console.log("My value: " + i);};
}

http://jsfiddle.net/7P6EN/

29 TravisJ Mar 06 2014 at 06:03

OPによって示されるコードの主な問題iは、2番目のループまで読み取られないことです。実例を示すために、コード内にエラーが表示されることを想像してください

funcs[i] = function() {            // and store them in funcs
    throw new Error("test");
    console.log("My value: " + i); // each should log its value.
};

The error actually does not occur until funcs[someIndex] is executed (). Using this same logic, it should be apparent that the value of i is also not collected until this point either. Once the original loop finishes, i++ brings i to the value of 3 which results in the condition i < 3 failing and the loop ending. At this point, i is 3 and so when funcs[someIndex]() is used, and i is evaluated, it is 3 - every time.

To get past this, you must evaluate i as it is encountered. Note that this has already happened in the form of funcs[i] (where there are 3 unique indexes). There are several ways to capture this value. One is to pass it in as a parameter to a function which is shown in several ways already here.

Another option is to construct a function object which will be able to close over the variable. That can be accomplished thusly

jsFiddle Demo

funcs[i] = new function() {   
    var closedVariable = i;
    return function(){
        console.log("My value: " + closedVariable); 
    };
};
24 Costa Dec 08 2016 at 00:33

JavaScript functions "close over" the scope they have access to upon declaration, and retain access to that scope even as variables in that scope change.

var funcs = []

for (var i = 0; i < 3; i += 1) {
  funcs[i] = function () {
    console.log(i)
  }
}

for (var k = 0; k < 3; k += 1) {
  funcs[k]()
}

Each function in the array above closes over the global scope (global, simply because that happens to be the scope they're declared in).

Later those functions are invoked logging the most current value of i in the global scope. That's the magic, and frustration, of closure.

"JavaScript Functions close over the scope they are declared in, and retain access to that scope even as variable values inside of that scope change."

Using let instead of var solves this by creating a new scope each time the for loop runs, creating a separated scope for each function to close over. Various other techniques do the same thing with extra functions.

var funcs = []

for (let i = 0; i < 3; i += 1) {
  funcs[i] = function () {
    console.log(i)
  }
}

for (var k = 0; k < 3; k += 1) {
  funcs[k]()
}

(let makes variables block scoped. Blocks are denoted by curly braces, but in the case of the for loop the initialization variable, i in our case, is considered to be declared in the braces.)

15 wpding Jul 14 2014 at 21:42

After reading through various solutions, I'd like to add that the reason those solutions work is to rely on the concept of scope chain. It's the way JavaScript resolve a variable during execution.

  • Each function definition forms a scope consisting of all the local variables declared by var and its arguments.
  • If we have inner function defined inside another (outer) function, this forms a chain, and will be used during execution
  • When a function gets executed, the runtime evaluates variables by searching the scope chain. If a variable can be found in a certain point of the chain it will stop searching and use it, otherwise it continues until the global scope reached which belongs to window.

In the initial code:

funcs = {};
for (var i = 0; i < 3; i++) {         
  funcs[i] = function inner() {        // function inner's scope contains nothing
    console.log("My value: " + i);    
  };
}
console.log(window.i)                  // test value 'i', print 3

When funcs gets executed, the scope chain will be function inner -> global. Since the variable i cannot be found in function inner (neither declared using var nor passed as arguments), it continues to search, until the value of i is eventually found in the global scope which is window.i.

By wrapping it in an outer function either explicitly define a helper function like harto did or use an anonymous function like Bjorn did:

funcs = {};
function outer(i) {              // function outer's scope contains 'i'
  return function inner() {      // function inner, closure created
   console.log("My value: " + i);
  };
}
for (var i = 0; i < 3; i++) {
  funcs[i] = outer(i);
}
console.log(window.i)          // print 3 still

When funcs gets executed, now the scope chain will be function inner -> function outer. This time i can be found in the outer function's scope which is executed 3 times in the for loop, each time has value i bound correctly. It won't use the value of window.i when inner executed.

More detail can be found here
It includes the common mistake in creating closure in the loop as what we have here, as well as why we need closure and the performance consideration.

13 PrithviUppalapati Nov 07 2016 at 18:25

With new features of ES6 block level scoping is managed:

var funcs = [];
for (let i = 0; i < 3; i++) {          // let's create 3 functions
    funcs[i] = function() {            // and store them in funcs
        console.log("My value: " + i); // each should log its value.
    };
}
for (let j = 0; j < 3; j++) {
    funcs[j]();                        // and now let's run each one to see
}

The code in OP's question is replaced with let instead of var.

10 ChristianLandgren Dec 10 2014 at 05:24

I'm surprised no one yet has suggested using the forEach function to better avoid (re)using local variables. In fact, I'm not using for(var i ...) at all anymore for this reason.

[0,2,3].forEach(function(i){ console.log('My value:', i); });
// My value: 0
// My value: 2
// My value: 3

// edited to use forEach instead of map.

9 jottos Apr 15 2009 at 13:18

The reason your original example did not work is that all the closures you created in the loop referenced the same frame. In effect, having 3 methods on one object with only a single i variable. They all printed out the same value.

9 sidhuko Jan 13 2018 at 20:17

This question really shows the history of JavaScript! Now we can avoid block scoping with arrow functions and handle loops directly from DOM nodes using Object methods.

const funcs = [1, 2, 3].map(i => () => console.log(i));
funcs.map(fn => fn())

const buttons = document.getElementsByTagName("button");
Object
  .keys(buttons)
  .map(i => buttons[i].addEventListener('click', () => console.log(i)));
<button>0</button><br>
<button>1</button><br>
<button>2</button>

8 AliKahoot Nov 04 2016 at 14:46

First of all, understand what's wrong with this code:

var funcs = [];
for (var i = 0; i < 3; i++) {          // let's create 3 functions
    funcs[i] = function() {            // and store them in funcs
        console.log("My value: " + i); // each should log its value.
    };
}
for (var j = 0; j < 3; j++) {
    funcs[j]();                        // and now let's run each one to see
}

Here when the funcs[] array is being initialized, i is being incremented, the funcs array is initialized and the size of func array becomes 3, so i = 3,. Now when the funcs[j]() is called, it is again using the variable i, which has already been incremented to 3.

Now to solve this, we have many options. Below are two of them:

  1. We can initialize i with let or initialize a new variable index with let and make it equal to i. So when the call is being made, index will be used and its scope will end after initialization. And for calling, index will be initialized again:

    var funcs = [];
    for (var i = 0; i < 3; i++) {          
        let index = i;
        funcs[i] = function() {            
            console.log("My value: " + index); 
        };
    }
    for (var j = 0; j < 3; j++) {
        funcs[j]();                        
    }
    
  2. Other Option can be to introduce a tempFunc which returns the actual function:

    var funcs = [];
    function tempFunc(i){
        return function(){
            console.log("My value: " + i);
        };
    }
    for (var i = 0; i < 3; i++) {  
        funcs[i] = tempFunc(i);                                     
    }
    for (var j = 0; j < 3; j++) {
        funcs[j]();                        
    }
    
8 VikashSingh Jan 20 2017 at 17:02

Use closure structure, this would reduce your extra for loop. You can do it in a single for loop:

var funcs = [];
for (var i = 0; i < 3; i++) {     
  (funcs[i] = function() {         
    console.log("My value: " + i); 
  })(i);
}
7 BimalDas Jan 16 2018 at 21:29

We will check , what actually happens when you declare var and let one by one.

Case1 : using var

<script>
   var funcs = [];
   for (var i = 0; i < 3; i++) {
     funcs[i] = function () {
        debugger;
        console.log("My value: " + i);
     };
   }
   console.log(funcs);
</script>

Now open your chrome console window by pressing F12 and refresh the page. Expend every 3 functions inside the array.You will see an property called [[Scopes]].Expand that one. You will see one array object called "Global",expand that one. You will find a property 'i' declared into the object which having value 3.

Conclusion:

  1. When you declare a variable using 'var' outside a function ,it becomes global variable(you can check by typing i or window.i in console window.It will return 3).
  2. The annominous function you declared will not call and check the value inside the function unless you invoke the functions.
  3. When you invoke the function , console.log("My value: " + i) takes the value from its Global object and display the result.

CASE2 : using let

Now replace the 'var' with 'let'

<script>
    var funcs = [];
    for (let i = 0; i < 3; i++) {
        funcs[i] = function () {
           debugger;
           console.log("My value: " + i);
        };
    }
    console.log(funcs);
</script>

Do the same thing, Go to the scopes . Now you will see two objects "Block" and "Global". Now expand Block object , you will see 'i' is defined there , and the strange thing is that , for every functions , the value if i is different (0 , 1, 2).

Conclusion:

When you declare variable using 'let' even outside the function but inside the loop , this variable will not be a Global variable , it will become a Block level variable which is only available for the same function only.That is the reason , we are getting value of i different for each function when we invoke the functions.

For more detail about how closer works , please go through the awesome video tutorial https://youtu.be/71AtaJpJHw0

4 RuneFS Jun 17 2015 at 19:02

You could use a declarative module for lists of data such as query-js(*). In these situations I personally find a declarative approach less surprising

var funcs = Query.range(0,3).each(function(i){
     return  function() {
        console.log("My value: " + i);
    };
});

You could then use your second loop and get the expected result or you could do

funcs.iterate(function(f){ f(); });

(*) I'm the author of query-js and therefor biased towards using it, so don't take my words as a recommendation for said library only for the declarative approach :)

4 RaxWunter Dec 17 2015 at 21:14

I prefer to use forEach function, which has its own closure with creating a pseudo range:

var funcs = [];

new Array(3).fill(0).forEach(function (_, i) { // creating a range
    funcs[i] = function() {            
        // now i is safely incapsulated 
        console.log("My value: " + i);
    };
});

for (var j = 0; j < 3; j++) {
    funcs[j](); // 0, 1, 2
}

That looks uglier than ranges in other languages, but IMHO less monstrous than other solutions.

4 pixel67 May 05 2016 at 18:48

And yet another solution: instead of creating another loop, just bind the this to the return function.

var funcs = [];

function createFunc(i) {
  return function() {
    console.log('My value: ' + i); //log value of i.
  }.call(this);
}

for (var i = 1; i <= 5; i++) {  //5 functions
  funcs[i] = createFunc(i);     // call createFunc() i=5 times
}

By binding this, solves the problem as well.

4 ShivangGupta May 02 2019 at 18:57

Till ES5, This problem can only be solved using closure.

But now in ES6, we have block level scope variables. Changing var to let in first for loop will solve the problem.

var funcs = [];
for (let i = 0; i < 3; i++) {      // let's create 3 functions
  funcs[i] = function() {          // and store them in funcs
    console.log("My value: " + i); // each should log its value.
  };
}
for (var j = 0; j < 3; j++) {
  funcs[j]();                      // and now let's run each one to see
}

3 Pawel Feb 28 2017 at 22:09

Many solutions seem correct but they don't mention it's called Currying which is a functional programming design pattern for situations like here. 3-10 times faster than bind depending on the browser.

var funcs = [];
for (var i = 0; i < 3; i++) {      // let's create 3 functions
  funcs[i] = curryShowValue(i);
}
for (var j = 0; j < 3; j++) {
  funcs[j]();                      // and now let's run each one to see
}

function curryShowValue(i) {
  return function showValue() {
    console.log("My value: " + i);
  }
}

See the performance gain in different browsers.

3 Buksy Nov 04 2016 at 15:58

Your code doesn't work, because what it does is:

Create variable `funcs` and assign it an empty array;  
Loop from 0 up until it is less than 3 and assign it to variable `i`;
    Push to variable `funcs` next function:  
        // Only push (save), but don't execute
        **Write to console current value of variable `i`;**

// First loop has ended, i = 3;

Loop from 0 up until it is less than 3 and assign it to variable `j`;
    Call `j`-th function from variable `funcs`:  
        **Write to console current value of variable `i`;**  
        // Ask yourself NOW! What is the value of i?

Now the question is, what is the value of variable i when the function is called? Because the first loop is created with the condition of i < 3, it stops immediately when the condition is false, so it is i = 3.

You need to understand that, in time when your functions are created, none of their code is executed, it is only saved for later. And so when they are called later, the interpreter executes them and asks: "What is the current value of i?"

So, your goal is to first save the value of i to function and only after that save the function to funcs. This could be done for example this way:

var funcs = [];
for (var i = 0; i < 3; i++) {          // let's create 3 functions
    funcs[i] = function(x) {            // and store them in funcs
        console.log("My value: " + x); // each should log its value.
    }.bind(null, i);
}
for (var j = 0; j < 3; j++) {
    funcs[j]();                        // and now let's run each one to see
}

This way, each function will have it's own variable x and we set this x to the value of i in each iteration.

This is only one of the multiple ways to solve this problem.

3 AshishYadav Jul 13 2018 at 15:02
var funcs = [];
for (var i = 0; i < 3; i++) {      // let's create 3 functions
  funcs[i] = function(param) {          // and store them in funcs
    console.log("My value: " + param); // each should log its value.
  };
}
for (var j = 0; j < 3; j++) {
  funcs[j](j);                      // and now let's run each one to see with j
}