javascript 可以从闭包访问可变变量。我怎样才能解决这个问题?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16724620/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Mutable variable is accessible from closure. How can I fix this?
提问by iCodeLikeImDrunk
I am using Typeahead by twitter. I am running into this warning from Intellij. This is causing the "window.location.href" for each link to be the last item in my list of items.
我在 Twitter 上使用 Typeahead。我遇到了来自 Intellij 的警告。这导致每个链接的“window.location.href”成为我的项目列表中的最后一个项目。
How can I fix my code?
我该如何修复我的代码?
Below is my code:
下面是我的代码:
AutoSuggest.prototype.config = function () {
var me = this;
var comp, options;
var gotoUrl = "/{0}/{1}";
var imgurl = '<img src="/icon/{0}.gif"/>';
var target;
for (var i = 0; i < me.targets.length; i++) {
target = me.targets[i];
if ($("#" + target.inputId).length != 0) {
options = {
source: function (query, process) { // where to get the data
process(me.results);
},
// set max results to display
items: 10,
matcher: function (item) { // how to make sure the result select is correct/matching
// we check the query against the ticker then the company name
comp = me.map[item];
var symbol = comp.s.toLowerCase();
return (this.query.trim().toLowerCase() == symbol.substring(0, 1) ||
comp.c.toLowerCase().indexOf(this.query.trim().toLowerCase()) != -1);
},
highlighter: function (item) { // how to show the data
comp = me.map[item];
if (typeof comp === 'undefined') {
return "<span>No Match Found.</span>";
}
if (comp.t == 0) {
imgurl = comp.v;
} else if (comp.t == -1) {
imgurl = me.format(imgurl, "empty");
} else {
imgurl = me.format(imgurl, comp.t);
}
return "\n<span id='compVenue'>" + imgurl + "</span>" +
"\n<span id='compSymbol'><b>" + comp.s + "</b></span>" +
"\n<span id='compName'>" + comp.c + "</span>";
},
sorter: function (items) { // sort our results
if (items.length == 0) {
items.push(Object());
}
return items;
},
// the problem starts here when i start using target inside the functions
updater: function (item) { // what to do when item is selected
comp = me.map[item];
if (typeof comp === 'undefined') {
return this.query;
}
window.location.href = me.format(gotoUrl, comp.s, target.destination);
return item;
}
};
$("#" + target.inputId).typeahead(options);
// lastly, set up the functions for the buttons
$("#" + target.buttonId).click(function () {
window.location.href = me.format(gotoUrl, $("#" + target.inputId).val(), target.destination);
});
}
}
};
With @cdhowie's help, some more code: i will update the updater and also the href for the click()
在@cdhowie 的帮助下,添加更多代码:我将更新更新程序以及 click() 的 href
updater: (function (inner_target) { // what to do when item is selected
return function (item) {
comp = me.map[item];
if (typeof comp === 'undefined') {
return this.query;
}
window.location.href = me.format(gotoUrl, comp.s, inner_target.destination);
return item;
}}(target))};
采纳答案by cdhowie
You need to nest two functions here, creating a new closure that captures the value of the variable (instead of the variable itself) at the moment the closure is created. You can do this using arguments to an immediately-invoked outer function. Replace this expression:
你需要在这里筑巢两个功能,创建一个新的闭包捕获变量(而不是变量本身)的值,此刻正在创建的闭包。您可以使用立即调用的外部函数的参数来执行此操作。替换这个表达式:
function (item) { // what to do when item is selected
comp = me.map[item];
if (typeof comp === 'undefined') {
return this.query;
}
window.location.href = me.format(gotoUrl, comp.s, target.destination);
return item;
}
With this:
有了这个:
(function (inner_target) {
return function (item) { // what to do when item is selected
comp = me.map[item];
if (typeof comp === 'undefined') {
return this.query;
}
window.location.href = me.format(gotoUrl, comp.s, inner_target.destination);
return item;
}
}(target))
Note that we pass target
into the outer function, which becomes the argument inner_target
, effectively capturing the value of target
at the moment the outer function is called. The outer function returns an inner function, which uses inner_target
instead of target
, and inner_target
will not change.
请注意,我们传入target
了外部函数,它成为了参数inner_target
,有效地捕获了target
调用外部函数时的值。外部函数返回一个内部函数,它使用inner_target
代替target
,并且inner_target
不会改变。
(Note that you can rename inner_target
to target
and you will be okay -- the closest target
will be used, which would be the function parameter. However, having two variables with the same name in such a tight scope could be very confusing and so I have named them differently in my example so that you can see what's going on.)
(请注意,您可以重命名inner_target
为target
,您会没事的 -target
将使用最接近的,这将是函数参数。但是,在如此严格的范围内拥有两个同名的变量可能会非常混乱,因此我命名为它们在我的示例中有所不同,以便您可以看到发生了什么。)
回答by Maciej Jankowski
I liked the paragraph Closures Inside Loopsfrom Javascript Garden
我喜欢的段落瓶盖内循环从使用Javascript花园
It explains three ways of doing it.
它解释了三种方法。
The wrong way of using a closure inside a loop
在循环中使用闭包的错误方法
for(var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
Solution 1with anonymous wrapper
使用匿名包装器的解决方案 1
for(var i = 0; i < 10; i++) {
(function(e) {
setTimeout(function() {
console.log(e);
}, 1000);
})(i);
}
Solution 2- returning a function from a closure
解决方案 2- 从闭包中返回一个函数
for(var i = 0; i < 10; i++) {
setTimeout((function(e) {
return function() {
console.log(e);
}
})(i), 1000)
}
Solution 3, my favorite, where I think I finally understood bind
- yaay! bind FTW!
解决方案 3,我最喜欢的,我想我终于明白了bind
-耶!绑定FTW!
for(var i = 0; i < 10; i++) {
setTimeout(console.log.bind(console, i), 1000);
}
I highly recommend Javascript garden- it showed me this and many more Javascript quirks (and made me like JS even more).
我强烈推荐Javascript 花园- 它向我展示了这一点以及更多 Javascript 怪癖(并使我更喜欢 JS)。
p.s. if your brain didn't melt you haven't had enough Javascript that day.
ps 如果你的大脑没有融化,那你那天的 Javascript 还不够多。
回答by Bogdan Ruzhitskiy
In ecmascript 6 we have new opportunities.
在 ecmascript 6 中,我们有了新的机会。
The letstatement declares a block scope local variable, optionally initializing it to a value. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
该让语句声明块范围局部变量,选择将其初始化为一个值。 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
回答by Oded Breiner
Since the only scoping that JavaScript has is functionscope, you can simply move the closure to an external function, outside of the scope you're in.
由于 JavaScript 的唯一作用域是函数作用域,因此您可以简单地将闭包移动到您所在作用域之外的外部函数。
回答by ivanhoe
Just to clarify on @BogdanRuzhitskiy answer (as I couldn't figure out how to add the code in a comment), the idea with using let is to create a local variable inside the for block:
只是为了澄清@BogdanRuzhitskiy 的回答(因为我不知道如何在评论中添加代码),使用 let 的想法是在 for 块中创建一个局部变量:
for(var i = 0; i < 10; i++) {
let captureI = i;
setTimeout(function() {
console.log(captureI);
}, 1000);
}
This will work in pretty much any modern browser except IE11.
这几乎适用于除 IE11 之外的所有现代浏览器。