JavaScript 范围(我认为?!)挑战

Scope (I think?!) challenge in JavaScript

使用 CreateJS,我输出了一个 canvas,其中包含各种对象的实例,以各种方式进行动画处理。几乎所有工作正常,除了一个问题,我 认为 是一个范围问题,但我不确定如何解决。我有一个由“_pulsars”引用的实例,包含一个对象的 11 个实例 'pulsar',代码运行如下:

function attachPulseFuncs() {

    var defaultFreq = 0.05; // controls the likelihood of a 'pulse' happening

    for (var i = 0; i < _pulsars.children.length; i++) {

        var myParent = _pulsars.children[i];

        myParent.isPulsing = false;

        _myParent.pulse = function() {
            if (myParent.isPulsing == false) {
                myParent.isPulsing = true;
                myParent.__frame = 1
                var tween = createjs.Tween.get(myParent).to({__frame:65}, 1500).call(myParent.resetPulse);
                tween.addEventListener("change", function() {
                myParent.gotoAndStop(myParent.__frame);
                });
            }
        }

        myParent.resetPulse = function() {
            myParent.gotoAndStop(1);
            myParent.isPulsing = false;
        }

        myParent.callRandomPulse = function() {
            var ran = Math.random();

            if (ran < freqNumber) {
                myParent.pulse();
            }
        }

        createjs.Ticker.addEventListener("tick", myParent.callRandomPulse);
    }
}

发生的情况是只有最后一个脉冲星有动画(无论组中有多少)。我想知道是不是因为只附加了一个事件侦听器?或者以某种方式将所有听众添加到一个 'pulsar'?请帮忙!

编辑:成功! 感谢 Robert(以及 Bergi 对类似问题的 link),帮助我更多地了解了闭包和解除绑定传递给函数的变量 - 正如另一个线程上的 link 所说:

"if we pass a parameter [the] function makes its own local copy of the variable (if it is not object type which pass by reference)"

这允许每个脉冲星对其父级有一个离散的引用,由 myParent 的每次迭代定义,而不是最终持有的单个外部范围引用myParent 的值。 (@Robert Koritnik 如果我误解了或者这是不正确的,请告诉我!谢谢你的帮助,如果我不是这个网站的新手,我会+1。希望很快)

您遇到的问题是,您迭代脉冲星并沿途递增 i 并添加这些事件侦听器。所以当你的 listers 真的开火时,他们使用 myParent (注意你可能的拼写错误,有时在它前面使用下划线,有时不使用)变量,当你的 for 循环完成时,它最后设置为最后一个脉冲星([= 的最后一个值13=]);

您必须更改这段代码

myParent.pulse = function() {
    if (myParent.isPulsing == false) {
        myParent.isPulsing = true;
        myParent.__frame = 1
        var tween = createjs.Tween.get(myParent).to({__frame:65}, 1500).call(myParent.resetPulse);
        tween.addEventListener("change", function() {
            myParent.gotoAndStop(myParent.__frame);
        });
    }
}

对此:

myParent.pulse = (function(parent) {
    return function() {
        if (parent.isPulsing === false) {
            parent.isPulsing = true;
            parent.__frame = 1
            var tween = createjs.Tween.get(parent).to({__frame:65}, 1500).call(parent.resetPulse);
            tween.addEventListener("change", function() {
                parent.gotoAndStop(parent.__frame);
            });
        }
    };
})(myParent);

此更改的作用是创建一个新的函数作用域,捕获 myParent 的当前值并将其存储在局部变量(参数)parent 中,然后返回一个使用这个新捕获值的函数。

也许我在这方面捕获的太多了,我应该只为 tween.addEventListener 函数调用创建一个新的函数范围。你应该知道哪一部分需要捕获。我的代码捕获的代码比可能需要的多得多,因此它应该也能正常工作,但过一会儿当您返回并引入一些更改时,它可能看起来很混乱。

因此将范围缩小到最小的语句集,例如:

myParent.pulse = function() {
    if (myParent.isPulsing == false) {
        myParent.isPulsing = true;
        myParent.__frame = 1
        var tween = createjs.Tween
            .get(myParent)
            .to({__frame:65}, 1500)
            .call(myParent.resetPulse);
        tween.addEventListener("change", (function(parent) {
            return function() {
                parent.gotoAndStop(parent.__frame);
            };
        })(myParent));
    }
}

但是如前所述,这可能不起作用,因为它使用最后一个 myParent 脉冲实例的 __frame 变量。所以你可能会得到我的第一个代码更改。