javaScript在循环内部关闭–简单的实践例子

发布于 2025-01-24 12:06:10 字数 2045 浏览 0 评论 0 原文

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>

…或异步代码,例如使用承诺:

// 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));
}

对于和循环的和,也很明显。

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 (const n of arr) {
  var obj = { number: n }; // or new MyLibObject({ ... })
  fns.push(() => console.log("n:", n, "|", "obj:", JSON.stringify(obj)));
}

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

解决这个基本问题的方法是什么?

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]();
}

It outputs this:

My value: 3
My value: 3
My value: 3

Whereas I'd like it to output:

My value: 0
My value: 1
My value: 2


The same problem occurs when the delay in running the function is caused by using event listeners:

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>

… or asynchronous code, e.g. using 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));
}

It is also apparent in for in and for of loops:

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 (const n of arr) {
  var obj = { number: n }; // or new MyLibObject({ ... })
  fns.push(() => console.log("n:", n, "|", "obj:", JSON.stringify(obj)));
}

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

What’s the solution to this basic problem?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(30

冬天旳寂寞 2025-01-31 12:06:11

这是一个简单的解决方案,使用 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
}

印刷:

 我的值:0
我的价值:1
我的价值:2
 

Here's a simple solution that uses forEach (works back to 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
}

Prints:

My value: 0
My value: 1
My value: 2
触ぅ动初心 2025-01-31 12:06:11

尝试此较短的一个

  • no array

  • no for Loop

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

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

http://jsfiddle.net/7p6en/

try this shorter one

  • no array

  • no extra for loop

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

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

http://jsfiddle.net/7P6EN/

小瓶盖 2025-01-31 12:06:11

OP显示的代码的主要问题是 i 在第二个循环之前永远不会读取。为了证明,想象一下在代码中看到一个错误,

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

直到执行 func [someIndex] ()之前,实际上不会发生错误。使用相同的逻辑,应该显然, i 的值也不会在此时收集。原始循环完成后, i ++ i 带入 3 的值,从而导致条件 i&lt; 3 失败和循环结束。此时, i 3 ,因此使用 funcs [someindex]()时, i is评估,每次都是3。

为了克服这一点,您必须在遇到的情况下评估 i 。请注意,这已经以 funcs [i] 的形式发生(其中有3个唯一的索引)。有几种捕获此值的方法。一种是将其作为参数传递到一个函数,该函数已在这里以几种方式显示。

另一个选项是构造一个功能对象,该对象将能够关闭变量。可以因此而实现

jsfiddle demo

funcs[i] = new function() {   
    var closedVariable = i;
    return function(){
        console.log("My value: " + closedVariable); 
    };
};

The main issue with the code shown by the OP is that i is never read until the second loop. To demonstrate, imagine seeing an error inside of the code

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); 
    };
};
听不够的曲调 2025-01-31 12:06:11

JavaScript函数“关闭”声明时可以访问的范围,并在该范围更改中保留对该范围的访问权限。

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]()
}

上面的数组中的每个函数都关闭了全局范围(全局,仅仅是因为这恰好是它们声明的范围)。

后来,这些功能被调用,以记录全局范围中 i 的最新值。那是封闭的魔力和沮丧。

“ JavaScript函数在声明其声明的范围上关闭,并保留对该范围的访问,即使在该范围内的变量值更改。”

使用 LET 而不是 var 每次运行循环运行<代码>时,通过创建一个新的范围来解决此问题,为每个函数创建一个分离的范围,以使每个函数关闭。其他各种技术都可以通过额外的功能来做同样的事情。

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 使变量blocked。在牙套中。)

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.)

此刻的回忆 2025-01-31 12:06:11

在阅读了各种解决方案之后,我想补充一点,这些解决方案起作用的原因是依靠示波器链的概念。这是JavaScript在执行过程中解决变量的方式。

  • 每个功能定义形成一个由所有本地组成的范围
    var 及其参数声明的变量。
  • 如果我们在另一个(外)函数中定义了内部函数,则
    形成链条,
  • 在执行函数时将在执行过程中使用,运行时将通过搜索示波器链来评估变量。如果可以在链的某个点中找到变量,它将停止搜索和使用它,否则它一直持续到属于 window 的全局范围为止。

在初始代码中:

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

当执行 funcs 时,范围链将是函数内部 - &gt;全局。由于变量 i 函数内Inner 中找不到 i 的of最终在 window.i 的全局范围中找到。

通过将其包装在外部函数中,要么明确定义辅助函数,例如 harto dig dig dig href =“ href =” ://stackoverflow.com/a/750495“> bjorn 做到了:

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

当执行 funcs 时,现在范围链将是函数inner -&gt;功能外部。这段时间 i 可以在外部函数的范围中找到,该范围在for循环中执行3次,每次都有值 i 正确绑定。内部执行后,它不会使用 window.i 的值。

可以找到更多详细信息在这里

它包括在循环中创建关闭的常见错误,就像我们在这里所拥有的那样,以及为什么我们需要封闭和绩效考虑。

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.

野心澎湃 2025-01-31 12:06:11

通过管理ES6块级范围的新功能:

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
}

OP问题中的代码替换为 而不是 var

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.

浪漫人生路 2025-01-31 12:06:11

我们将检查,当您声明 var
一一。

case1 使用 var

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

现在按下 Chrome Console窗口,按下 f12 并刷新页面。
花费数组内的每3个函数。你会看到一个
数组对象称为“ Global” ,展开一个。您会找到一个属性'i'在具有值3的对象中,

  1. > 使用'var'函数外部的变量,它变为全局变量(您可以通过键入 i
    window.i 在控制台窗口中。它将返回3)。
  2. 您声明的匿名函数将不会调用并检查功能中的值,除非您调用
    功能。
  3. 调用函数时
    结果。

case2:使用让

现在使用'var' '让'

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

做同样的事情,转到范围。现在,您将看到两个对象“ block” “ global” 。现在展开对象,您
将看到“我”在那里定义,奇怪的是,对于每个功能,如果 i 是不同的值(0、1、2)。

结论:

使用声明变量,'让'甚至在函数之外,但在循环内,此变量将不是全局
变量,它将成为一个 block 级别变量,仅适用于同一函数。这是原因,我们
当我们调用函数时,每个功能的值都在 i 中获得不同。

有关近距离工作方式的更多详细信息,请浏览很棒的视频教程 https://youtu.be/71atajpjhw0

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.

enter image description here

enter image description here

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 anonymous 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).

enter image description here

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

辞慾 2025-01-31 12:06:11

我很惊讶任何人都没有建议使用 foreach 函数,以更好地避免使用局部变量。实际上,由于这个原因,我不再使用(var i ...)的

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

//编辑为使用 foreach 而不是地图。

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.

离笑几人歌 2025-01-31 12:06:11

您的原始示例不起作用的原因是,您在循环中创建的所有关闭都引用了同一帧。实际上,在一个对象上只有3种方法,只有一个 i 变量。他们都打印出相同的值。

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.

你没皮卡萌 2025-01-31 12:06:11

这个问题确实显示了JavaScript的历史!现在,我们可以避免使用箭头函数进行块范围,并使用对象方法直接从DOM节点处理循环。

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>

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>

一城柳絮吹成雪 2025-01-31 12:06:11

首先,了解此代码的问题:

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
}

在此处,当 funcs [] 正在初始化数组时, i 正在增加, funcs 数组是初始化的,并且 func 数组的大小变为3,因此 i = 3,
现在,当调用 funcs [j]()时,它再次使用变量 i ,该变量已经增加到3。

现在解决这个问题,我们有很多选项。以下是其中两个:

  1. 我们可以使用 初始化 或初始化新变量 index 让让 >使其等于 i 。因此,在呼叫时,将使用索引,其范围将在初始化后结束。为了打电话,索引将再次初始化:

      var funcs = [];
    for(var i = 0; i&lt; 3; i ++){          
        令索引= i;
        funcs [i] = function(){            
            console.log(“我的值:” + index); 
        };
    }
    for(var j = 0; j&lt; 3; j ++){
        funcs [j]();                        
    }
     
  2. 其他选项可以是引入 tempfunc 返回实际功能:

      var funcs = [];
    功能tempfunc(i){
        返回函数(){
            console.log(“我的值:” + i);
        };
    }
    for(var i = 0; i&lt; 3; i ++){  
        funcs [i] = tempfunc(i);                                     
    }
    for(var j = 0; j&lt; 3; j ++){
        funcs [j]();                        
    }
     

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]();                        
    }
    
梦在深巷 2025-01-31 12:06:11

使用闭合结构,这将减少额外的循环。您可以单一进行循环:

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

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);
}
悲凉≈ 2025-01-31 12:06:11

直到ES5,只能使用闭合解决此问题。

但是现在在ES6中,我们有块级范围变量。将 var 更改为在loop 的第一个中将解决问题。

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
}

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
}

囍笑 2025-01-31 12:06:11

如果您在循环时遇到了这种问题,而不是循环的,例如:

var i = 0;
while (i < 5) {
  setTimeout(function() {
    console.log(i);
  }, i * 1000);
  i++;
}

关闭当前值的技术有些不同。在 block中声明 const 内部的块分布变量,然后将当前 i 分配给它。然后,无论何时使用该变量,都可以将 i 替换为新的块scoped变量:

var i = 0;
while (i < 5) {
  const thisIterationI = i;
  setTimeout(function() {
    console.log(thisIterationI);
  }, i * 1000);
  i++;
}

对于不支持块型变量的较旧浏览器,您可以使用 i 的IIFE:

var i = 0;
while (i < 5) {
  (function(innerI) {
    setTimeout(function() {
      console.log(innerI);
    }, innerI * 1000);
  })(i);
  i++;
}

如果要调用的异步操作恰好是 settimeout 如上所述,您还可以使用 third> third> third 参数来调用 settimeout ,以指示参数使用以下方式调用传递的功能:

var i = 0;
while (i < 5) {
  setTimeout(
    (thisIterationI) => { // Callback
      console.log(thisIterationI);
    },
    i * 1000, // Delay
    i // Gets passed to the callback; becomes thisIterationI
  );
  i++;
}

If you're having this sort of problem with a while loop, rather than a for loop, for example:

var i = 0;
while (i < 5) {
  setTimeout(function() {
    console.log(i);
  }, i * 1000);
  i++;
}

The technique to close over the current value is a bit different. Declare a block-scoped variable with const inside the while block, and assign the current i to it. Then, wherever the variable is being used asynchronously, replace i with the new block-scoped variable:

var i = 0;
while (i < 5) {
  const thisIterationI = i;
  setTimeout(function() {
    console.log(thisIterationI);
  }, i * 1000);
  i++;
}

For older browsers that don't support block-scoped variables, you can use an IIFE called with i:

var i = 0;
while (i < 5) {
  (function(innerI) {
    setTimeout(function() {
      console.log(innerI);
    }, innerI * 1000);
  })(i);
  i++;
}

If the asynchronous action to be invoked happens to be setTimeout like the above, you can also call setTimeout with a third parameter to indicate the argument to call the passed function with:

var i = 0;
while (i < 5) {
  setTimeout(
    (thisIterationI) => { // Callback
      console.log(thisIterationI);
    },
    i * 1000, // Delay
    i // Gets passed to the callback; becomes thisIterationI
  );
  i++;
}

三寸金莲 2025-01-31 12:06:11

您可以使用声明的模块来列表数据列表,例如 query-js (*)。在这些情况下,我个人发现一种声明性的方法并不令人惊讶,

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

您可以使用第二个循环并获得预期的结果,或者您可以做

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

(*)我是查询JS的作者,因此有偏见,所以不要以我的话语作为对声明性方法的上述图书馆的建议:)

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 :)

素食主义者 2025-01-31 12:06:11

我更喜欢使用 foreach 函数,它可以通过创建伪范围的封闭方式进行封闭:

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
}

它看起来比其他语言中的范围更丑,但比其他解决方案更可怕。

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.

┈┾☆殇 2025-01-31 12:06:11

还有另一个解决方案:只需将 绑定到返回函数,而不是创建另一个循环。

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
}

通过绑定 这个 ,也解决了问题。

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.

GRAY°灰色天空 2025-01-31 12:06:11

使用Let(阻止Scope)而不是VAR。

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

Use let(blocked-scope) instead of var.

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

离旧人 2025-01-31 12:06:11

您的代码不起作用,因为它的作用是:

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?

现在的问题是,当调用函数时,变量 i 的值是什么?因为第一个循环是在 i&lt的条件下创建的。 3 ,条件是错误的立即停止,因此是 i = 3

您需要了解,在创建函数时,没有执行它们的代码,只能保存以后。因此,当稍后调用它们时,解释器会执行它们并问:“ i 的当前值是多少?”

因此,您的目标是首先将 i 的值保存到功能,然后将功能保存到 funcs 之后。例如,可以这样做:

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
}

这样,每个函数将具有其自己的变量 x ,我们将此 x 设置为 i 在每次迭代中。

这只是解决此问题的多种方法之一。

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.

放赐 2025-01-31 12:06:10

好吧,问题在于,在您的每个匿名函数中,变量 i 都绑定到函数之外的相同变量。

ES6解决方案:

ecmascript 6(es6)引入了新的 LET const 关键字,这些关键字的范围与 var -基于变量。例如,在带有 LET 基于索引的循环中,通过循环的每次迭代都将具有带有循环范围的新变量 i ,因此您的代码将按照您的期望工作。有很多资源,但我建议 2性块的块帖子作为重要的信息来源。

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

但是请注意,Edge 14支持之前的IE9-IE11和Edge ,但要弄错了(它们并不是每次创建新的 i ,所以所有上面的功能将像我们使用 var 一样记录3。 Edge 14终于正确。


ES5.1解决方案:

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工作,则 $。每个()函数为您提供了类似的功能。


经典解决方案:关闭

您要做的是将每个函数中的变量绑定到函数之外的单独的,不变的值:

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”的值保持在您的预期状态。

Well, the problem is that the variable i, within each of your anonymous functions, is bound to the same variable outside of the function.

ES6 solution: let

ECMAScript 6 (ES6) introduces new let and const keywords that are scoped differently than var-based variables. For example, in a loop with a let-based index, each iteration through the loop will have a new variable i with loop scope, so your code would work as you expect. There are many resources, but I'd recommend 2ality's block-scoping post as a great source of information.

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

Beware, though, that IE9-IE11 and Edge prior to Edge 14 support let but get the above wrong (they don't create a new i each time, so all the functions above would log 3 like they would if we used var). Edge 14 finally gets it right.


ES5.1 solution: forEach

With the relatively widespread availability of the Array.prototype.forEach function (in 2015), it's worth noting that in those situations involving iteration primarily over an array of values, .forEach() provides a clean, natural way to get a distinct closure for every iteration. That is, assuming you've got some sort of array containing values (DOM references, objects, whatever), and the problem arises of setting up callbacks specific to each element, you can do this:

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

The idea is that each invocation of the callback function used with the .forEach loop will be its own closure. The parameter passed in to that handler is the array element specific to that particular step of the iteration. If it's used in an asynchronous callback, it won't collide with any of the other callbacks established at other steps of the iteration.

If you happen to be working in jQuery, the $.each() function gives you a similar capability.


Classic solution: Closures

What you want to do is bind the variable within each function to a separate, unchanging value outside of the function:

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]();
}

Since there is no block scope in JavaScript - only function scope - by wrapping the function creation in a new function, you ensure that the value of "i" remains as you intended.

鸵鸟症 2025-01-31 12:06: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]();
}

edit (2014):

我个人认为 @aust 有关使用 .bind 现在是做这种事情的最佳方法。当您不需要或不需要与 bind> bind 's thisarg 混乱时,还有lo-dash/underscore的 _。partial

Try:

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]();
}

Edit (2014):

Personally I think @Aust's more recent answer about using .bind is the best way to do this kind of thing now. There's also lo-dash/underscore's _.partial when you don't need or want to mess with bind's thisArg.

久而酒知 2025-01-31 12:06:10

尚未提及的另一种方法是使用 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]();
}

Update

正如@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]();
}

Another way that hasn't been mentioned yet is the use of 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]();
}

UPDATE

As pointed out by @squint and @mekdev, you get better performance by creating the function outside the loop first and then binding the results within the loop.

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]();
}

小草泠泠 2025-01-31 12:06:10

使用立即In-invoked函数表达式,是封闭索引可变的最简单且最可读的方法:

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);

}

这将迭代器 i 发送到我们将其定义为 index 的匿名函数。这会创建一个封闭,其中变量 i 被保存,以便以后在IIFE内的任何异步功能中使用。

Using an Immediately-Invoked Function Expression, the simplest and most readable way to enclose an index variable:

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);

}

This sends the iterator i into the anonymous function of which we define as index. This creates a closure, where the variable i gets saved for later use in any asynchronous functionality within the IIFE.

不再让梦枯萎 2025-01-31 12:06:10

聚会很晚,但是我今天在探索这个问题,并注意到许多答案并没有完全解决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 。来自

重要: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 变量不会碰撞并相互覆盖。一些次要的优化给出了许多其他用户给出的最终答案:

//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现在的主流,我们现在可以使用新的关键字来创建块划分变量:

//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]();
}

看看现在有多容易!有关更多信息,请参见这个答案,我的信息基于。

Bit late to the party, but I was exploring this issue today and noticed that many of the answers don't completely address how Javascript treats scopes, which is essentially what this boils down to.

So as many others mentioned, the problem is that the inner function is referencing the same i variable. So why don't we just create a new local variable each iteration, and have the inner function reference that instead?

//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]();
}

Just like before, where each inner function outputted the last value assigned to i, now each inner function just outputs the last value assigned to ilocal. But shouldn't each iteration have it's own ilocal?

Turns out, that's the issue. Each iteration is sharing the same scope, so every iteration after the first is just overwriting ilocal. From MDN:

Important: JavaScript does not have block scope. Variables introduced with a block are scoped to the containing function or script, and the effects of setting them persist beyond the block itself. In other words, block statements do not introduce a scope. Although "standalone" blocks are valid syntax, you do not want to use standalone blocks in JavaScript, because they don't do what you think they do, if you think they do anything like such blocks in C or Java.

Reiterated for emphasis:

JavaScript does not have block scope. Variables introduced with a block are scoped to the containing function or script

We can see this by checking ilocal before we declare it in each iteration:

//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;
}

This is exactly why this bug is so tricky. Even though you are redeclaring a variable, Javascript won't throw an error, and JSLint won't even throw a warning. This is also why the best way to solve this is to take advantage of closures, which is essentially the idea that in Javascript, inner functions have access to outer variables because inner scopes "enclose" outer scopes.

Closures

This also means that inner functions "hold onto" outer variables and keep them alive, even if the outer function returns. To utilize this, we create and call a wrapper function purely to make a new scope, declare ilocal in the new scope, and return an inner function that uses ilocal (more explanation below):

//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]();
}

Creating the inner function inside a wrapper function gives the inner function a private environment that only it can access, a "closure". Thus, every time we call the wrapper function we create a new inner function with it's own separate environment, ensuring that the ilocal variables don't collide and overwrite each other. A few minor optimizations gives the final answer that many other SO users gave:

//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);
    };
}

Update

With ES6 now mainstream, we can now use the new let keyword to create block-scoped variables:

//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]();
}

Look how easy it is now! For more information see this answer, which my info is based off of.

弃爱 2025-01-31 12:06:10

随着ES6现在得到广泛支持,该问题的最佳答案发生了变化。 ES6提供 LET const 关键字为此确切情况。我们只能使用设置循环范围变量,而不是闭合闭合:

var funcs = [];

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

然后, val 将指向特定循环特定转弯的对象,并将返回正确的值而无需其他闭合表示法。显然,这大大简化了这个问题。

const Let 相似,其附加限制是,在初始分配后,变量名不能反弹为新的参考。

现在,针对浏览器浏览器的那些浏览器支持。 const /当前在最新的Firefox,Safari,Edge和Chrome中支持。它还在节点中支持,您可以利用Babel等构建工具来将其使用。您可以在此处看到一个有效的示例: http:/ http:/jsfiddle.net/ben336/rbu4t/rbu4t/rbu4t/2/2/ a>

docs在这里:

当心当意边缘14支持但弄错了上述(它们并不是每次创建新的 i ),因此上面的所有功能都会像我们使用的那样记录3 var )。 Edge 14终于正确。

With ES6 now widely supported, the best answer to this question has changed. ES6 provides the let and const keywords for this exact circumstance. Instead of messing around with closures, we can just use let to set a loop scope variable like this:

var funcs = [];

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

val will then point to an object that is specific to that particular turn of the loop, and will return the correct value without the additional closure notation. This obviously significantly simplifies this problem.

const is similar to let with the additional restriction that the variable name can't be rebound to a new reference after initial assignment.

Browser support is now here for those targeting the latest versions of browsers. const/let are currently supported in the latest Firefox, Safari, Edge and Chrome. It also is supported in Node, and you can use it anywhere by taking advantage of build tools like Babel. You can see a working example here: http://jsfiddle.net/ben336/rbU4t/2/

Docs here:

Beware, though, that IE9-IE11 and Edge prior to Edge 14 support let but get the above wrong (they don't create a new i each time, so all the functions above would log 3 like they would if we used var). Edge 14 finally gets it right.

迷你仙 2025-01-31 12:06:10

另一种说法是您功能中的 i 在执行功能时是绑定的,而不是创建函数的时间。

创建闭合时, i 是对外部范围中定义的变量的引用,而不是创建闭合时的副本。将在执行时进行评估。

其他大多数答案通过创建另一个不会改变您的价值的变量来提供解决方法。

只是以为我会添加一个解释,以清楚地说。就解决方案而言,我会选择Harto的解决方案,因为这是从这里的答案中做到的最不言自明的方式。发布的任何代码都可以使用,但是我选择关闭工厂而不是写一堆评论来解释为什么我要宣布新变量(Freddy和1800),或者有怪异的嵌入式闭合语法(AppHacker)。

Another way of saying it is that the i in your function is bound at the time of executing the function, not the time of creating the function.

When you create the closure, i is a reference to the variable defined in the outside scope, not a copy of it as it was when you created the closure. It will be evaluated at the time of execution.

Most of the other answers provide ways to work around by creating another variable that won't change the value for you.

Just thought I'd add an explanation for clarity. For a solution, personally, I'd go with Harto's since it is the most self-explanatory way of doing it from the answers here. Any of the code posted will work, but I'd opt for a closure factory over having to write a pile of comments to explain why I'm declaring a new variable(Freddy and 1800's) or have weird embedded closure syntax(apphacker).

深爱不及久伴 2025-01-31 12:06:10

您需要了解的是JavaScript中变量的范围是基于函数。这是一个重要的区别,而不是说c#的块范围,而只是将变量复制到for for will will的一个。

将其包裹在评估返回函数的函数中,就像AppHacker的答案一样,因为变量现在具有函数范围,因此可以解决问题。

还有一个让关键字而不是var,可以使用块范围规则。在这种情况下,定义for内部变量的方法将解决问题。就是说,由于兼容性,让关键字不是实用的解决方案。

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]();
}

What you need to understand is the scope of the variables in javascript is based on the function. This is an important difference than say c# where you have block scope, and just copying the variable to one inside the for will work.

Wrapping it in a function that evaluates returning the function like apphacker's answer will do the trick, as the variable now has the function scope.

There is also a let keyword instead of var, that would allow using the block scope rule. In that case defining a variable inside the for would do the trick. That said, the let keyword isn't a practical solution because of compatibility.

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]();
}

月寒剑心 2025-01-31 12:06:10

这是该技术的另一个变体,类似于Bjorn的(AppHacker),它使您可以在功能内部分配变量值,而不是将其传递为参数,有时可能更清晰:

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

请注意,无论您使用哪种技术, index 变量变成了一种静态变量,绑定到内部函数的返回副本。即,在呼叫之间保留其价值的更改。它可能非常方便。

Here's another variation on the technique, similar to Bjorn's (apphacker), which lets you assign the variable value inside the function rather than passing it as a parameter, which might be clearer sometimes:

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

Note that whatever technique you use, the index variable becomes a sort of static variable, bound to the returned copy of the inner function. I.e., changes to its value are preserved between calls. It can be very handy.

夜血缘 2025-01-31 12:06:10

这描述了在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 的新副本
也创建以引用新对象。因此, counter1 counter2 彼此独立。

使用循环中的闭合

在循环中封闭非常棘手。

考虑:

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

请注意,计数器[0] counters [1] 不是独立的。实际上,它们在相同的 obj 上运行!

这是因为在循环的所有迭代中共享 obj 的副本,也许是出于性能原因。
即使 {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);
  }
}

这起作用是因为函数范围中的局部变量以及函数参数变量被分配了
进入时新副本。

This describes the common mistake with using closures in JavaScript.

A function defines a new environment

Consider:

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

For each time makeCounter is invoked, {counter: 0} results in a new object being created. Also, a new copy of obj
is created as well to reference the new object. Thus, counter1 and counter2 are independent of each other.

Closures in loops

Using a closure in a loop is tricky.

Consider:

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

Notice that counters[0] and counters[1] are not independent. In fact, they operate on the same obj!

This is because there is only one copy of obj shared across all iterations of the loop, perhaps for performance reasons.
Even though {counter: 0} creates a new object in each iteration, the same copy of obj will just get updated with a
reference to the newest object.

Solution is to use another helper function:

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);
  }
}

This works because local variables in the function scope directly, as well as function argument variables, are allocated
new copies upon entry.

屋檐 2025-01-31 12:06:10

最简单的解决方案是,

而不是使用:

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次。这是因为在循环中创建的匿名函数,共享相同的闭合,并且在该闭合中, 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循环的身体封装在(立即发出的函数表达式),并将 new_i 作为参数,并以 i 捕获。由于立即执行匿名函数,因此在匿名函数中定义的每个函数的 i 值都不同。

该解决方案似乎符合任何此类问题,因为它需要最小的更改此问题的原始代码。实际上,这是根据设计,根本不应该是一个问题!

The most simple solution would be,

Instead of using:

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

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

which alerts "2", for 3 times. This is because anonymous functions created in for loop, shares same closure, and in that closure, the value of i is the same. Use this to prevent shared closure:

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]();
}

The idea behind this is, encapsulating the entire body of the for loop with an IIFE (Immediately-Invoked Function Expression) and passing new_i as a parameter and capturing it as i. Since the anonymous function is executed immediately, the i value is different for each function defined inside the anonymous function.

This solution seems to fit any such problem since it will require minimal changes to the original code suffering from this issue. In fact, this is by design, it should not be an issue at all!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文