为循环中的每个元素分配不同的eventListeners时,它们都运行相同的函数,DESPITE使用.bind()和闭包

时间:2015-08-20 10:42:07

标签: javascript canvas closures addeventlistener createjs

问题

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

创建练习时,将从数据库加载JSON数据。它添加了用户指定的所有内容,但在添加区域时存在问题。这些是用户定义的,包括坐标,大小以及单击时要执行的操作。它通过循环执行此操作,检查要添加的区域类型(圆形,矩形,隐藏矩形等),通过CreateJS添加它,然后分配eventListener以执行预定义操作。但是,最后,所有eventListeners都执行相同的操作,即 last 。但是,我使用.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(),在这种情况下,所有字母都会播放声音&#34; c.mp3&#34; 而不是他们各自的声音。我哪里错了? eventListener是否会在某处被覆盖?

我尝试了什么

我尝试修改将eventListener添加到以下内容的代码:

rectangleHidden.on('click', function (scene, action) {
                    return scene.doActions.bind(event, action, scene);
                }(this, areas[i].actions));

但它仍会产生相同的结果。

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

function saveNumber(value) {
    return function () { console.log(value); };
}

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

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

其他信息

我可以补充说这个指的是Canvas-stage本身,我最初没有编写这段代码,我只是想解决这个问题。在我从 createjs-2013.12.12 更新到 createjs-2015.05.21 之前,相同的代码似乎一直在工作,但切换回它没有任何区别。如果您需要,请向我询问更多详情!

我已经阅读了JavaScript closure inside loops – simple practical example,虽然它看起来似乎可以帮助我乍一看,但它并没有解决我的问题。在答案中解释了如何使用闭包和.bind()来解决问题,但是,我的问题是,尽管使用了,但仍然会出现

1 个答案:

答案 0 :(得分:0)

问题确实存在于CreateJS并更新到最新版本。之前的类是通过使用initialize-method进行扩展的,后来改为使用SuperClass_constructor,如解释here.

由于旧代码,按钮未正确创建并导致.on()和.addEventListener()方法出现问题。循环一直在工作!