闭包的问题.任何提示
Closures issue. Any tips?
我有一个问题,下面的代码是与闭包相关的,我需要一些帮助。
正如你所看到的,我在for循环中创建了几个图像,我分配了不同的id(即。数组中的数字)。到目前为止,一切顺利。当我点击不同的图像时,我希望函数showId被调用,图像id作为参数,但问题是,用作函数参数的数字总是变成nr 8(数组中的最后一个数字)。我怎么解决这个问题?
提前感谢。
var imageArea = document.getElementById('imageArea');
var nrArray = [1,2,3,4,5,6,7,8];
for (var i = 0; i < nrArray.length; i++){
var image = document.createElement('img');
image.src = "images/theimage.png";
image.id = nrArray[i];
imgArea.appendChild(image);
image.addEventListener ('click',function() {showId(image.id);},false);
}
对于这个完全相同的问题,stackoverflow上有无数的答案,我将在一分钟内找到一些,但这里是关键点:
- Javascript有词法作用域(不是动态作用域)
- Javascript中最小的Javascript作用域是
function
- Block 没有有自己的作用域 Javascript使用变量提升,这意味着在作用域执行之前找到作用域中的所有变量,并将其移动到作用域的开头。也就是说
- 函数是一级对象,这意味着它们被当作变量对待,可以像这样分配和传递
- 你可以通过创建一个自动执行的匿名函数来为变量创建一个稳定的作用域
var image = ...
不在你想的地方。只有一个image
变量,它(由于词法作用域)是你在闭包中访问的变量,它在循环结束时显然会指向最后一个迭代项。例如:
(function(localImage) {
image.addEventListener ('click',function() {showId(localImage.id);},false);
})(image);
另外,正如其他人指出的那样,事件侦听器闭包是在上下文中执行的。因此,反过来,不必担心使用close来修复作用域,您可以这样做:
image.addEventListener ('click',function() {showId(this.id);},false);
编辑
一些类似问题的链接和对答案的不同观点:
- 在子函数中访问循环迭代?
- for循环和词法环境中的闭包
- JavaScript中奇怪的东西" for " Javascript循环中的事件处理程序-需要闭包吗?
通过使用this
关键字引用父对象来访问ID:
//In this case, this refers to the object that owns the function, i.e., your img
image.addEventListener ('click',function() {showId(this.id);},false);
为什么不用this
呢?
var imgArea = document.getElementById('imageArea');
var nrArray = [1,2,3,4,5,6,7,8];
for (var i = 0; i < nrArray.length; i++) {
var image = document.createElement('img');
image.src = "images/theimage.png";
image.id = nrArray[i];
imgArea.appendChild(image);
image.addEventListener('click', function() {
showId(this.id);
}, false);
}
另外,你最初的问题是关于创建闭包,所以即使我确信使用this
将提供您现在需要的,我还是要添加一个创建新作用域的小演示,使您能够利用闭包来完成相同的任务:
var imgArea = document.getElementById("imageArea");
var nrArray = [1, 2, 3, 4, 5, 6, 7, 8];
for (var i = 0; i < nrArray.length; i++) {
createClosure(i);
}
function createClosure(i) {
var image = document.createElement('img');
image.src = "images/theimage.png";
image.id = nrArray[i];
imgArea.appendChild(image);
image.addEventListener('click', function () {
showId(image.id);
}, false);
}
jsFiddle demo: http://jsfiddle.net/HMYsW/1/
James Hill的回答指出了解决这种情况的简单方法,但当然,您仍然存在闭包问题。一旦您想要在事件侦听器中使用的数据作为环境的一部分不可用(例如,作为this
的属性),您将再次遇到同样的问题。
要解决这个问题,您需要创建一个新的闭包(即function
):
function createListener(id) {
return function() { showId(id); }
}
var imageArea = document.getElementById('imageArea');
for (var i = 1; i <= 8; i++){
var image = document.createElement('img');
image.src = "images/theimage.png";
image.id = i;
imgArea.appendChild(image);
image.addEventListener ('click', createListener(image.id), false);
}
这里有一个创建并返回另一个函数的函数。返回的函数将是实际的事件监听器。在创建过程中,变量id
被关闭并保持其值。当然你可以一步完成:
image.addEventListener ('click', function (id) {
return function() { showId(id); };
}(image.id), false);
一旦代码超出作用域,Javascript不会自动从内存中删除变量。这意味着即使你在一个特定的范围内声明变量,在你的例子中是for循环,在这个范围之外,变量仍然存在。
附加到所有图像的事件侦听器正在执行$showId(image.id);
行然而,由于变量没有从内存中删除,变量image
仍然存在,即for循环的最后一个。这就是为什么你总是从数组中得到最后一个数字:8。
当eventlistener被调用时,它将变量this
设置为自己的元素,因此将变量从image更改为this将解决问题。addEventListener调用变成:
image.addEventListener ('click',function() {showId(image.id);},false);
- Graphiti中是否有任何工具提示功能
- 如何访问高图表工具提示中的任何特定数据
- 当单击任何其他工具提示时,无法打开uib工具提示
- 使用 jQuery 工具提示插件(或任何其他方法)显示带有 PHP 对象属性的工具提示
- 获得取消的 JavaScript 提示不执行任何操作
- 如果用户在收到提示后将任何信息留空,我如何从 Javascript 的输出 (window.alert) 中排除该数据
- 只要单击页面上的任何内容,引导工具提示就会停止工作
- 是否有任何关于让HTML5画布在IE9中工作的提示
- 基础工具提示不起作用.关于这个主题的任何想法
- 如何在JSDOC中标记任何类类型以获取类型提示
- 有没有任何方法可以创建带有两个输入字段的提示
- 有没有任何方法可以在Dreamweaver中自动显示javascript的代码提示
- 启动工具提示在任何其他事件运行后停止工作
- 闭包的问题.任何提示
- 是否有任何方法显示工具提示默认(没有悬停饼图)在chartjs
- 是否有可能提示用户输入任何grunt任务
- 解析用户保存抛出错误,没有任何提示
- 是否有任何jquery插件来生成提示图像
- 是否有浏览器支持或任何插件,显示html元素的ID或名称作为工具提示,而悬停在一个元素
- JavaScript:关于在打开/关闭菜单时改进此代码的任何提示