当为循环中的每个元素分配不同的事件监听器时,它们都 运行 相同的功能,尽管使用了 .bind() 和闭包
When assigning different eventListeners to each element in a loop they all run the same function, DESPITE use of .bind() and closures
问题
该项目使用 CreateJS,构建后您可以通过添加 JSON 数据在 Canvas 中创建各种游戏和练习。为了演示这个问题,我将使用 'Alphabet Board' 的示例,您可以在其中单击每个字母的图片以听到大声朗读。问题是它们都播放最后一个字母的声音。
创建练习时,JSON-数据从数据库加载。它添加了用户指定的所有内容,但是在添加区域时出现问题。这些是用户定义的坐标、大小以及单击时要执行的操作。它通过一个循环来实现这一点,检查要添加的区域类型(圆形、矩形、隐藏矩形等),通过 CreateJS 添加它,然后分配一个 eventListener 来执行预定义的操作。然而,最后,所有的事件监听器执行相同的动作,即最后一个。但是,我正在使用 .bind() 并且还尝试在具有相同结果的匿名函数中创建闭包。
例子
一个区域的数据是这样的(以字母A为例):
{"coords":"25,30,67,55","type":"rectangleHidden","actions":[{"do":"playSound","data":{"asset":{"id":"a","src":"a.mp3"}}}]}
您可以在这里使用 JSON 在线编辑器更好地查看:
Data for A to C
所有资产都已预加载并可访问。加载所有资产后,构建场景并运行此函数:
p.addAreas = function (areas, event) {
var i = 0,
length = (typeof areas != 'undefined') ? areas.length : 0;
for (; i < length; i++) {
var x = i;
switch (areas[i].type) {
case 'rectangleHidden':
var rectangleHidden = new ui.Rectangle(areas[i].coords, this.STRIKE, this.FILL, this.STROKE_STYLE, false);
rectangleHidden.alpha = 0.01;
this.addChild(rectangleHidden);
rectangleHidden.addEventListener('click', this.doActions.bind(event, areas[i].actions, this));
break;
default :
break;
}
}
}
我省略了开关的不同情况,因为这个例子只使用了 rectangleHidden。下一个也是如此,我只包含了 playSound。所以addEventListener分配的函数是这个:
p.doActions = function (actions, scene) {
"use strict";
var i = 0,
length = actions.length;
// cleans up scene
p.cleanUp(scene);
// adds actions in sequence order
for (; i < length; i++) {
var action = actions[i],
data = action.data;
// switch over action
switch (action.do) {
case 'playSound':
var delayTime = Number(data.delay || 0);
var looper = Number(data.loop || 0);
new createjs.Sound.play(data.asset.id, { delay: delayTime, loop: looper });
break;
default:
break;
}
}
}
即使使用了.bind(),在这种情况下,所有的字母都会播放声音"c.mp3" 而不是他们各自的声音。我哪里错了? eventListener 是否在某处被覆盖?
我试过的
我尝试修改添加 eventListener 的代码,如下所示:
rectangleHidden.on('click', function (scene, action) {
return scene.doActions.bind(event, action, scene);
}(this, areas[i].actions));
但它仍然产生相同的结果。
我还尝试在另一个项目中创建一个经典闭包,其中事件监听器使用循环中的 i 值调用此函数:
function saveNumber(value) {
return function () { console.log(value); };
}
在另一个项目中,正如预期的那样,每个列表项在单击时都会从控制台的数组中写入它们的 自己的 编号。当我尝试将此函数添加到当前项目中的事件监听器时,它仍然每次都写出循环的最后一个值,在本例中为 2。
rectangleHidden.addEventListener('click', saveNumber(i));
它们之间有什么区别?(该函数是在循环外和 addAreas 表达式外定义的。)
附加信息
我可以补充一点 this 指的是 Canvas-stage 本身,我最初没有写这段代码,我只是想解决这个问题。在我从 createjs-2013.12.12 更新到 createjs-2015.05.21 之前,相同的代码似乎一直在工作,但切换回它确实没有任何区别。有需要的可以私信我哦!
我已阅读 JavaScript closure inside loops – simple practical example,虽然乍一看它似乎可以帮助我,但它并没有完全解决我的问题。在答案中解释了如何使用闭包和 .bind() 来解决问题,但是,我的问题是这种情况正在发生尽管使用那些。
问题确实与 CreateJS 和更新到最新版本有关。以前 类 是通过使用初始化方法扩展的,后来改为使用 SuperClass_constructor,如 here.
所述
由于旧代码,未正确创建按钮并导致 .on() 和 .addEventListener() 方法出现问题。循环一直有效!
问题
该项目使用 CreateJS,构建后您可以通过添加 JSON 数据在 Canvas 中创建各种游戏和练习。为了演示这个问题,我将使用 'Alphabet Board' 的示例,您可以在其中单击每个字母的图片以听到大声朗读。问题是它们都播放最后一个字母的声音。
创建练习时,JSON-数据从数据库加载。它添加了用户指定的所有内容,但是在添加区域时出现问题。这些是用户定义的坐标、大小以及单击时要执行的操作。它通过一个循环来实现这一点,检查要添加的区域类型(圆形、矩形、隐藏矩形等),通过 CreateJS 添加它,然后分配一个 eventListener 来执行预定义的操作。然而,最后,所有的事件监听器执行相同的动作,即最后一个。但是,我正在使用 .bind() 并且还尝试在具有相同结果的匿名函数中创建闭包。
例子
一个区域的数据是这样的(以字母A为例):
{"coords":"25,30,67,55","type":"rectangleHidden","actions":[{"do":"playSound","data":{"asset":{"id":"a","src":"a.mp3"}}}]}
您可以在这里使用 JSON 在线编辑器更好地查看: Data for A to C
所有资产都已预加载并可访问。加载所有资产后,构建场景并运行此函数:
p.addAreas = function (areas, event) {
var i = 0,
length = (typeof areas != 'undefined') ? areas.length : 0;
for (; i < length; i++) {
var x = i;
switch (areas[i].type) {
case 'rectangleHidden':
var rectangleHidden = new ui.Rectangle(areas[i].coords, this.STRIKE, this.FILL, this.STROKE_STYLE, false);
rectangleHidden.alpha = 0.01;
this.addChild(rectangleHidden);
rectangleHidden.addEventListener('click', this.doActions.bind(event, areas[i].actions, this));
break;
default :
break;
}
}
}
我省略了开关的不同情况,因为这个例子只使用了 rectangleHidden。下一个也是如此,我只包含了 playSound。所以addEventListener分配的函数是这个:
p.doActions = function (actions, scene) {
"use strict";
var i = 0,
length = actions.length;
// cleans up scene
p.cleanUp(scene);
// adds actions in sequence order
for (; i < length; i++) {
var action = actions[i],
data = action.data;
// switch over action
switch (action.do) {
case 'playSound':
var delayTime = Number(data.delay || 0);
var looper = Number(data.loop || 0);
new createjs.Sound.play(data.asset.id, { delay: delayTime, loop: looper });
break;
default:
break;
}
}
}
即使使用了.bind(),在这种情况下,所有的字母都会播放声音"c.mp3" 而不是他们各自的声音。我哪里错了? eventListener 是否在某处被覆盖?
我试过的
我尝试修改添加 eventListener 的代码,如下所示:
rectangleHidden.on('click', function (scene, action) {
return scene.doActions.bind(event, action, scene);
}(this, areas[i].actions));
但它仍然产生相同的结果。
我还尝试在另一个项目中创建一个经典闭包,其中事件监听器使用循环中的 i 值调用此函数:
function saveNumber(value) {
return function () { console.log(value); };
}
在另一个项目中,正如预期的那样,每个列表项在单击时都会从控制台的数组中写入它们的 自己的 编号。当我尝试将此函数添加到当前项目中的事件监听器时,它仍然每次都写出循环的最后一个值,在本例中为 2。
rectangleHidden.addEventListener('click', saveNumber(i));
它们之间有什么区别?(该函数是在循环外和 addAreas 表达式外定义的。)
附加信息
我可以补充一点 this 指的是 Canvas-stage 本身,我最初没有写这段代码,我只是想解决这个问题。在我从 createjs-2013.12.12 更新到 createjs-2015.05.21 之前,相同的代码似乎一直在工作,但切换回它确实没有任何区别。有需要的可以私信我哦!
我已阅读 JavaScript closure inside loops – simple practical example,虽然乍一看它似乎可以帮助我,但它并没有完全解决我的问题。在答案中解释了如何使用闭包和 .bind() 来解决问题,但是,我的问题是这种情况正在发生尽管使用那些。
问题确实与 CreateJS 和更新到最新版本有关。以前 类 是通过使用初始化方法扩展的,后来改为使用 SuperClass_constructor,如 here.
所述由于旧代码,未正确创建按钮并导致 .on() 和 .addEventListener() 方法出现问题。循环一直有效!