Javascript 当为循环中的每个元素分配不同的EventListener时,它们都运行相同的函数,尽管使用了.bind()和闭包 问题

Javascript 当为循环中的每个元素分配不同的EventListener时,它们都运行相同的函数,尽管使用了.bind()和闭包 问题,javascript,canvas,closures,addeventlistener,createjs,Javascript,Canvas,Closures,Addeventlistener,Createjs,该项目使用CreateJS,其构建目的是通过添加JSON数据在画布中创建各种游戏和练习。为了演示这个问题,我将使用“字母板”的例子,在这个例子中,你可以点击每个字母的图片,听到它被大声读出。问题是它们都播放最后一个字母的声音 创建练习时,JSON数据从数据库加载。它会添加用户指定的所有内容,但在添加区域时会出现问题。这些是用户定义的坐标、大小以及单击时要执行的操作。它通过循环、检查要添加的区域类型(圆形、矩形、隐藏矩形等)、通过CreateJS添加,然后分配eventListener来执行预定义

该项目使用CreateJS,其构建目的是通过添加JSON数据在画布中创建各种游戏和练习。为了演示这个问题,我将使用“字母板”的例子,在这个例子中,你可以点击每个字母的图片,听到它被大声读出。问题是它们都播放最后一个字母的声音

创建练习时,JSON数据从数据库加载。它会添加用户指定的所有内容,但在添加区域时会出现问题。这些是用户定义的坐标、大小以及单击时要执行的操作。它通过循环、检查要添加的区域类型(圆形、矩形、隐藏矩形等)、通过CreateJS添加,然后分配eventListener来执行预定义的操作来实现。但是,最终,所有EventListener都执行相同的操作,即最后一个操作。但是,我使用的是.bind(),并尝试在匿名函数中创建闭包,结果相同

例子 这是区域数据的样子(例如字母A):

您可以使用JSON编辑器在线查看这里:

所有资产都已预加载并可访问。加载所有资源后,将生成场景并运行此功能:

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;
        }
    }
}
但它仍然产生同样的结果

我还尝试在另一个项目中创建一个经典闭包,其中eventlistener使用循环中的I值调用此函数:

function saveNumber(value) {
    return function () { console.log(value); };
}
在另一个项目中,正如预期的那样,单击时,每个列表项都会从控制台中的数组中写入自己的编号。当我尝试将此函数添加到当前项目中的eventlistener时,它仍然每次都会写出循环的最后一个值,在本例中为2。
rectangleHidden.addEventListener('click',saveNumber(i))

它们之间的区别是什么?(函数在循环外部和addAreas表达式外部定义。)

附加信息 我可以补充说,这个指的是画布阶段本身,我最初并没有编写这段代码,我只是试图解决这个问题。在我从createjs-2013.12.12更新到createjs-2015.05.21之前,相同的代码似乎一直在工作,但切换回它并没有任何区别。如果你需要的话,请问我更多的细节


我读过,虽然乍一看它似乎可以帮助我,但它并没有完全解决我的问题。答案中解释了如何使用闭包和.bind()来解决这个问题,然而,我的问题是,尽管使用了闭包和.bind(),但这种情况还是发生了。

问题确实出在CreateJS和更新到最新版本上。如前所述,以前的类是通过使用initialize方法扩展的,后来改为使用超类_构造函数


由于旧代码的原因,按钮没有正确创建,并导致.on()和.addEventListener()方法出现问题。循环一直有效

问题确实出在CreateJS和更新到最新版本上。如前所述,以前的类是通过使用initialize方法扩展的,后来改为使用超类_构造函数


由于旧代码的原因,按钮没有正确创建,并导致.on()和.addEventListener()方法出现问题。循环一直有效

一个提示:播放声音时不要使用“new”。返回一个实例。JSON是如何解析的?在运行
addAreas
逻辑之前,是否已验证JavaScript对象已解析为预期格式?只是声音在所有情况下都是一样的吗?如果在for循环中调试/登录,值是否显示预期的mp3?这不是CreateJS特有的问题,正如下面这个简单的示例所证明的:正如Lanny所建议的,您的数据似乎不正确。作为补充说明,CreateJS支持通过“on”进行绑定,如下所示:我删除了
new
,谢谢!是的,JSON被正确解析并按预期工作。不,不仅仅是声音,还有整个束缚函数。在循环内部,值的行为符合要求,但不知何故,最后分配的函数覆盖了所有其他函数。我也尝试过使用“on”而不是“addEventListener”,但结果还是一样的。我开始怀疑这是否与CreateJS中的EventDispatcher有关。不是EventDispatcher本身,但我认为它可能在项目中使用/分配不正确。请注意:播放声音时不要使用“new”。返回一个实例。JSON是如何解析的?在运行
addAreas
逻辑之前,是否已验证JavaScript对象已解析为预期格式?只是声音在所有情况下都是一样的吗?如果在for循环中调试/登录,值是否显示预期的mp3?这不是CreateJS特有的问题,正如下面这个简单的示例所证明的:正如Lanny所建议的,您的数据似乎不正确。作为补充说明,CreateJS支持通过“on”进行绑定,如下所示:我删除了
new
,谢谢!是的,JSON被正确解析并按预期工作。不,不仅仅是声音,还有整个束缚函数。在循环内部,值的行为符合要求,但不知何故,最后分配的函数覆盖了所有其他函数。我也尝试过使用“on”而不是“addEventListener”,但结果还是一样的。我开始怀疑这是否与CreateJS中的EventDispatcher有关,而不是EventDispatcher本身,但我认为它可能在项目中使用/分配不正确。
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;
        }
    }
}
rectangleHidden.on('click', function (scene, action) {
                    return scene.doActions.bind(event, action, scene);
                }(this, areas[i].actions));
function saveNumber(value) {
    return function () { console.log(value); };
}