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

Scope (I think?!) challenge in JavaScript

本文关键字:挑战 范围 JavaScript      更新时间:2023-09-26

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

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);
    }
}

发生的事情是,只有最后一颗脉冲星会发光(无论群体中有多少)。我想知道是否是因为只附加了一个事件侦听器?还是以某种方式将所有听众都添加到一个"脉冲星"中?请帮忙!

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

"如果我们传递一个参数,函数会制作自己的本地副本 变量(如果不是通过引用传递的对象类型)"

这允许每个脉冲星有一个离散的母体参考,由myParent的每次迭代定义,而不是由myParent的最终值持有的单个外范围参考。(@Robert 科里特尼克 如果我误解或这是不正确的,请告诉我!感谢您的帮助,如果我对该网站不是那么陌生,我会 +1。希望很快)

你遇到的问题是你迭代你的脉冲星,沿途递增i并添加这些事件侦听器。因此,当您的列表器实际触发时,他们会使用 myParent(请注意您可能的拼写错误有时在它前面使用下划线,有时不使用)变量,该变量在您的 for 循环完成时最后设置为最后一个脉冲星(最后一个值为 i);

您必须更改这段代码

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));
    }
}

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