javaScript在循环内部关闭–简单的实践例子
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();
}
解决这个基本问题的方法是什么?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(30)
这是一个简单的解决方案,使用
foreach
(回到IE9):印刷:
Here's a simple solution that uses
forEach
(works back to IE9):Prints:
尝试此较短的一个
no array
no for Loop
http://jsfiddle.net/7p6en/
try this shorter one
no array
no extra for loop
http://jsfiddle.net/7P6EN/
OP显示的代码的主要问题是
i
在第二个循环之前永远不会读取。为了证明,想象一下在代码中看到一个错误,直到执行
func [someIndex]
()之前,实际上不会发生错误。使用相同的逻辑,应该显然,i
的值也不会在此时收集。原始循环完成后,i ++
将i
带入3
的值,从而导致条件i&lt; 3
失败和循环结束。此时,i
是3
,因此使用funcs [someindex]()
时,i
is评估,每次都是3。为了克服这一点,您必须在遇到的情况下评估
i
。请注意,这已经以funcs [i]
的形式发生(其中有3个唯一的索引)。有几种捕获此值的方法。一种是将其作为参数传递到一个函数,该函数已在这里以几种方式显示。另一个选项是构造一个功能对象,该对象将能够关闭变量。可以因此而实现
jsfiddle demo
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 codeThe error actually does not occur until
funcs[someIndex]
is executed()
. Using this same logic, it should be apparent that the value ofi
is also not collected until this point either. Once the original loop finishes,i++
bringsi
to the value of3
which results in the conditioni < 3
failing and the loop ending. At this point,i
is3
and so whenfuncs[someIndex]()
is used, andi
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 offuncs[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
JavaScript函数“关闭”声明时可以访问的范围,并在该范围更改中保留对该范围的访问权限。
上面的数组中的每个函数都关闭了全局范围(全局,仅仅是因为这恰好是它们声明的范围)。
后来,这些功能被调用,以记录全局范围中
i
的最新值。那是封闭的魔力和沮丧。“ JavaScript函数在声明其声明的范围上关闭,并保留对该范围的访问,即使在该范围内的变量值更改。”
使用
LET
而不是var
每次运行循环运行<代码>时,通过创建一个新的范围来解决此问题,为每个函数创建一个分离的范围,以使每个函数关闭。其他各种技术都可以通过额外的功能来做同样的事情。(
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.
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 ofvar
solves this by creating a new scope each time thefor
loop runs, creating a separated scope for each function to close over. Various other techniques do the same thing with extra functions.(
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.)在阅读了各种解决方案之后,我想补充一点,这些解决方案起作用的原因是依靠示波器链的概念。这是JavaScript在执行过程中解决变量的方式。
由
var
及其参数
声明的变量。形成链条,
window
的全局范围为止。在初始代码中:
当执行
funcs
时,范围链将是函数内部 - &gt;全局
。由于变量i
在函数内Inner
中找不到i
的of最终在window.i
的全局范围中找到。通过将其包装在外部函数中,要么明确定义辅助函数,例如 harto dig dig dig href =“ href =” ://stackoverflow.com/a/750495“> bjorn 做到了:
当执行
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.
variables declared by
var
and itsarguments
.forms a chain, and will be used during execution
window
.In the initial code:
When
funcs
gets executed, the scope chain will befunction inner -> global
. Since the variablei
cannot be found infunction inner
(neither declared usingvar
nor passed as arguments), it continues to search, until the value ofi
is eventually found in the global scope which iswindow.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:
When
funcs
gets executed, now the scope chain will befunction inner -> function outer
. This timei
can be found in the outer function's scope which is executed 3 times in the for loop, each time has valuei
bound correctly. It won't use the value ofwindow.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.
通过管理ES6块级范围的新功能:
OP问题中的代码替换为
让
而不是var
。With new features of ES6 block level scoping is managed:
The code in OP's question is replaced with
let
instead ofvar
.case1 :使用
var
现在按下 Chrome Console窗口,按下 f12 并刷新页面。
花费数组内的每3个函数。你会看到一个
数组对象称为
“ Global”
,展开一个。您会找到一个属性'i'
在具有值3的对象中,'var'
函数外部的变量,它变为全局变量(您可以通过键入i
或window.i
在控制台窗口中。它将返回3)。功能。
结果。
case2:使用让
现在使用
'var'
'让'
做同样的事情,转到范围。现在,您将看到两个对象
“ block”
和“ global”
。现在展开块
对象,您将看到“我”在那里定义,奇怪的是,对于每个功能,如果
i
是不同的值(0、1、2)。结论:
使用
声明变量,'让'
甚至在函数之外,但在循环内,此变量将不是全局变量,它将成为一个
block
级别变量,仅适用于同一函数。这是原因,我们当我们调用函数时,每个功能的值都在
i
中获得不同。有关近距离工作方式的更多详细信息,请浏览很棒的视频教程 https://youtu.be/71atajpjhw0
Case1 : using
var
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 onearray object called
"Global"
,expand that one. You will find a property'i'
declared into the object which having value 3.Conclusion:
'var'
outside a function ,it becomes global variable(you can check by typingi
orwindow.i
in console window.It will return 3).functions.
console.log("My value: " + i)
takes the value from itsGlobal
object and display theresult.
CASE2 : using let
Now replace the
'var'
with'let'
Do the same thing, Go to the scopes . Now you will see two objects
"Block"
and"Global"
. Now expandBlock
object , youwill 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 Globalvariable , it will become a
Block
level variable which is only available for the same function only.That is the reason , weare 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
我很惊讶任何人都没有建议使用 foreach 函数,以更好地避免使用局部变量。实际上,由于这个原因,我不再使用(var i ...)的
。
//编辑为使用
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 usingfor(var i ...)
at all anymore for this reason.// edited to use
forEach
instead of map.您的原始示例不起作用的原因是,您在循环中创建的所有关闭都引用了同一帧。实际上,在一个对象上只有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.这个问题确实显示了JavaScript的历史!现在,我们可以避免使用箭头函数进行块范围,并使用对象方法直接从DOM节点处理循环。
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.
首先,了解此代码的问题:
在此处,当
funcs []
正在初始化数组时,i
正在增加,funcs
数组是初始化的,并且func
数组的大小变为3,因此i = 3,
。现在,当调用
funcs [j]()
时,它再次使用变量i
,该变量已经增加到3。现在解决这个问题,我们有很多选项。以下是其中两个:
我们可以使用
初始化 或初始化新变量
index
让让 >使其等于i
。因此,在呼叫时,将使用索引
,其范围将在初始化后结束。为了打电话,索引
将再次初始化:其他选项可以是引入
tempfunc
返回实际功能:First of all, understand what's wrong with this code:
Here when the
funcs[]
array is being initialized,i
is being incremented, thefuncs
array is initialized and the size offunc
array becomes 3, soi = 3,
.Now when the
funcs[j]()
is called, it is again using the variablei
, which has already been incremented to 3.Now to solve this, we have many options. Below are two of them:
We can initialize
i
withlet
or initialize a new variableindex
withlet
and make it equal toi
. 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:Other Option can be to introduce a
tempFunc
which returns the actual function:使用闭合结构,这将减少额外的循环。您可以单一进行循环:
Use closure structure, this would reduce your extra for loop. You can do it in a single for loop:
直到ES5,只能使用闭合解决此问题。
但是现在在ES6中,我们有块级范围变量。将 var 更改为让在loop 的第一个中将解决问题。
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.
如果您在循环时遇到了这种问题,而不是循环的
,例如:
关闭当前值的技术有些不同。在 block中声明
const
内部的块分布变量,然后将当前i
分配给它。然后,无论何时使用该变量,都可以将i
替换为新的块scoped变量:对于不支持块型变量的较旧浏览器,您可以使用
i
的IIFE:如果要调用的异步操作恰好是
settimeout
如上所述,您还可以使用 third> third> third 参数来调用settimeout
,以指示参数使用以下方式调用传递的功能:If you're having this sort of problem with a
while
loop, rather than afor
loop, for example:The technique to close over the current value is a bit different. Declare a block-scoped variable with
const
inside thewhile
block, and assign the currenti
to it. Then, wherever the variable is being used asynchronously, replacei
with the new block-scoped variable:For older browsers that don't support block-scoped variables, you can use an IIFE called with
i
:If the asynchronous action to be invoked happens to be
setTimeout
like the above, you can also callsetTimeout
with a third parameter to indicate the argument to call the passed function with:您可以使用声明的模块来列表数据列表,例如 query-js (*)。在这些情况下,我个人发现一种声明性的方法并不令人惊讶,
您可以使用第二个循环并获得预期的结果,或者您可以做
(*)我是查询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
You could then use your second loop and get the expected result or you could do
(*) 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 :)
我更喜欢使用
foreach
函数,它可以通过创建伪范围的封闭方式进行封闭:它看起来比其他语言中的范围更丑,但比其他解决方案更可怕。
I prefer to use
forEach
function, which has its own closure with creating a pseudo range:That looks uglier than ranges in other languages, but IMHO less monstrous than other solutions.
还有另一个解决方案:只需将 绑定到返回函数,而不是创建另一个循环。
通过绑定 这个 ,也解决了问题。
And yet another solution: instead of creating another loop, just bind the
this
to the return function.By binding this, solves the problem as well.
使用Let(阻止Scope)而不是VAR。
Use let(blocked-scope) instead of var.
您的代码不起作用,因为它的作用是:
现在的问题是,当调用函数时,变量
i
的值是什么?因为第一个循环是在i&lt的条件下创建的。 3
,条件是错误的立即停止,因此是i = 3
。您需要了解,在创建函数时,没有执行它们的代码,只能保存以后。因此,当稍后调用它们时,解释器会执行它们并问:“
i
的当前值是多少?”因此,您的目标是首先将
i
的值保存到功能,然后将功能保存到funcs
之后。例如,可以这样做:这样,每个函数将具有其自己的变量
x
,我们将此x
设置为i 在每次迭代中。
这只是解决此问题的多种方法之一。
Your code doesn't work, because what it does is:
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 ofi < 3
, it stops immediately when the condition is false, so it isi = 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 tofuncs
. This could be done for example this way:This way, each function will have it's own variable
x
and we set thisx
to the value ofi
in each iteration.This is only one of the multiple ways to solve this problem.
好吧,问题在于,在您的每个匿名函数中,变量
i
都绑定到函数之外的相同变量。ES6解决方案:
让
ecmascript 6(es6)引入了新的
LET
和const
关键字,这些关键字的范围与var
-基于变量。例如,在带有LET
基于索引的循环中,通过循环的每次迭代都将具有带有循环范围的新变量i
,因此您的代码将按照您的期望工作。有很多资源,但我建议 2性块的块帖子作为重要的信息来源。但是请注意,Edge 14支持之前的IE9-IE11和Edge ,但要弄错了(它们并不是每次创建新的
i
,所以所有上面的功能将像我们使用var
一样记录3。 Edge 14终于正确。ES5.1解决方案:
与
array.prototype.foreach
函数相对广泛的可用性(在2015年),值得注意的是,在这些情况下,涉及迭代的情况主要是在一系列价值观上,> .foreach()
提供了一种干净,自然的方法,可为每次迭代均可闭合。也就是说,假设您有某种数组包含值(DOM引用,对象,任何),并且出现了设置特定于每个元素的回调的问题,则可以做到这一点:想法是每个调用调用回调
.foreach
循环使用的功能将是其自己的闭合。传递给该处理程序的参数是特定于迭代的特定步骤的数组元素。如果它在异步回调中使用,则不会与在迭代其他步骤中建立的其他回调相撞。如果您碰巧在jQuery工作,则
$。每个()
函数为您提供了类似的功能。经典解决方案:关闭
您要做的是将每个函数中的变量绑定到函数之外的单独的,不变的值:
由于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
andconst
keywords that are scoped differently thanvar
-based variables. For example, in a loop with alet
-based index, each iteration through the loop will have a new variablei
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.Beware, though, that IE9-IE11 and Edge prior to Edge 14 support
let
but get the above wrong (they don't create a newi
each time, so all the functions above would log 3 like they would if we usedvar
). 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: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:
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.
尝试:
edit (2014):
我个人认为 @aust 有关使用
.bind
现在是做这种事情的最佳方法。当您不需要或不需要与
thisarg 混乱时,还有lo-dash/underscore的bind> bind
's_。partial
。Try:
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 withbind
'sthisArg
.尚未提及的另一种方法是使用
function.prototype.bind
Update
正如@Squint和@mekdev指出的那样,您可以通过首先在循环之外创建功能,然后将结果绑定到循环中,从而获得更好的性能。
Another way that hasn't been mentioned yet is the use of
Function.prototype.bind
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.
使用立即In-invoked函数表达式,是封闭索引可变的最简单且最可读的方法:
这将迭代器
i
发送到我们将其定义为index
的匿名函数。这会创建一个封闭,其中变量i
被保存,以便以后在IIFE内的任何异步功能中使用。Using an Immediately-Invoked Function Expression, the simplest and most readable way to enclose an index variable:
This sends the iterator
i
into the anonymous function of which we define asindex
. This creates a closure, where the variablei
gets saved for later use in any asynchronous functionality within the IIFE.聚会很晚,但是我今天在探索这个问题,并注意到许多答案并没有完全解决JavaScript如何处理范围,这实际上是归结为归结为。
因此,正如许多其他提到的问题,问题在于,内部函数正在引用相同的
i
变量。那么,为什么我们不只是创建每个局部变量的新局部变量,而是将内部函数引用呢?就像以前一样,每个内部函数输出了分配给
i
的最后一个值,现在每个内部函数仅输出分配给iLocal
的最后一个值。但是,每个迭代都不应该拥有自己的ilocal
吗?事实证明,这就是问题。每个迭代都共享相同的范围,因此第一个迭代在第一个迭代之后只是覆盖
iLocal
。来自重申强调:
我们可以通过在每次迭代中声明
ilocal
来查看它:这就是为什么此错误如此棘手的原因。即使您正在重新启动变量,JavaScript也不会出错,JSlint甚至不会发出警告。这也是解决此问题的最佳方法是利用闭合的原因,这实际上是在JavaScript中,内部功能可以访问外部变量,因为内部范围“包围”外部范围。
这也意味着内部函数“保持“外部变量”并保持它们的生命,即使外部函数返回。为了利用它,我们创建和调用包装器函数纯粹是为了制作新范围,在新范围中声明
iLocal
,然后返回使用ilocal
的内部函数(更多说明)以下):在包装器函数中创建内部函数可为内部函数提供一个私有环境,只有它才能访问,即“闭合”。因此,每次我们调用包装器函数时,我们都会使用其自己的独立环境创建一个新的内部函数,以确保
ilocal
变量不会碰撞并相互覆盖。一些次要的优化给出了许多其他用户给出的最终答案:更新
使用ES6现在的主流,我们现在可以使用新的
让
关键字来创建块划分变量:看看现在有多容易!有关更多信息,请参见这个答案,我的信息基于。
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?Just like before, where each inner function outputted the last value assigned to
i
, now each inner function just outputs the last value assigned toilocal
. But shouldn't each iteration have it's ownilocal
?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:Reiterated for emphasis:
We can see this by checking
ilocal
before we declare it in each iteration: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.
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 usesilocal
(more explanation below):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:Update
With ES6 now mainstream, we can now use the new
let
keyword to create block-scoped variables:Look how easy it is now! For more information see this answer, which my info is based off of.
随着ES6现在得到广泛支持,该问题的最佳答案发生了变化。 ES6提供
LET
和const
关键字为此确切情况。我们只能使用让
设置循环范围变量,而不是闭合闭合:然后,
val
将指向特定循环特定转弯的对象,并将返回正确的值而无需其他闭合表示法。显然,这大大简化了这个问题。const
与Let
相似,其附加限制是,在初始分配后,变量名不能反弹为新的参考。现在,针对浏览器浏览器的那些浏览器支持。
const
/让
当前在最新的Firefox,Safari,Edge和Chrome中支持。它还在节点中支持,您可以利用Babel等构建工具来将其使用。您可以在此处看到一个有效的示例: http:/ http:/jsfiddle.net/ben336/rbu4t/rbu4t/rbu4t/2/2/ a>docs在这里:
当心当意边缘14支持
让
但弄错了上述(它们并不是每次创建新的i
),因此上面的所有功能都会像我们使用的那样记录3var
)。 Edge 14终于正确。With ES6 now widely supported, the best answer to this question has changed. ES6 provides the
let
andconst
keywords for this exact circumstance. Instead of messing around with closures, we can just uselet
to set a loop scope variable like this: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 tolet
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 newi
each time, so all the functions above would log 3 like they would if we usedvar
). Edge 14 finally gets it right.另一种说法是您功能中的
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).
您需要了解的是JavaScript中变量的范围是基于函数。这是一个重要的区别,而不是说c#的块范围,而只是将变量复制到for for will will的一个。
将其包裹在评估返回函数的函数中,就像AppHacker的答案一样,因为变量现在具有函数范围,因此可以解决问题。
还有一个让关键字而不是var,可以使用块范围规则。在这种情况下,定义for内部变量的方法将解决问题。就是说,由于兼容性,让关键字不是实用的解决方案。
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.
这是该技术的另一个变体,类似于Bjorn的(AppHacker),它使您可以在功能内部分配变量值,而不是将其传递为参数,有时可能更清晰:
请注意,无论您使用哪种技术,
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:
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.这描述了在JavaScript中使用闭合的常见错误。
一个函数定义了一个新的环境,
请考虑:
每次调用
makecounter
,{counter:0}
会导致创建的新对象。另外,obj
的新副本也创建以引用新对象。因此,
counter1
和counter2
彼此独立。使用循环中的闭合
在循环中封闭非常棘手。
考虑:
请注意,
计数器[0]
和counters [1]
是不是独立的。实际上,它们在相同的obj
上运行!这是因为在循环的所有迭代中共享
obj
的副本,也许是出于性能原因。即使
{counter:0}
在每个迭代中创建一个新对象,但同一副本的obj
只会使用一个更新参考最新对象。
解决方案是使用另一个辅助功能:
这起作用是因为函数范围中的局部变量以及函数参数变量被分配了
进入时新副本。
This describes the common mistake with using closures in JavaScript.
A function defines a new environment
Consider:
For each time
makeCounter
is invoked,{counter: 0}
results in a new object being created. Also, a new copy ofobj
is created as well to reference the new object. Thus,
counter1
andcounter2
are independent of each other.Closures in loops
Using a closure in a loop is tricky.
Consider:
Notice that
counters[0]
andcounters[1]
are not independent. In fact, they operate on the sameobj
!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 ofobj
will just get updated with areference to the newest object.
Solution is to use another helper function:
This works because local variables in the function scope directly, as well as function argument variables, are allocated
new copies upon entry.
最简单的解决方案是,
而不是使用:
要警报“ 2”,持续3次。这是因为在循环中创建的匿名函数,共享相同的闭合,并且在该闭合中,
i
的值是相同的。使用它来防止共享的封闭:这背后的想法是,将整个for循环的身体封装在(立即发出的函数表达式),并将
new_i
作为参数,并以i
捕获。由于立即执行匿名函数,因此在匿名函数中定义的每个函数的i
值都不同。该解决方案似乎符合任何此类问题,因为它需要最小的更改此问题的原始代码。实际上,这是根据设计,根本不应该是一个问题!
The most simple solution would be,
Instead of using:
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: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 asi
. Since the anonymous function is executed immediately, thei
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!