4

Javascriptのforループ内でsetTimeout()関数を実行しようとしていますが、定義されていても「shape is undefined」というエラーが発生し、それを関数内のパラメーターとして渡します。 setTimeout()呼び出し。setTimeoutエンクロージャーを削除すると、この関数は正常に機能します。

なぜこのエラーが発生するのですか?どうすれば修正できますか?

ありがとう!

function fadeShapes(layer, movement, opacity, speed) {
    var shapes = layer.getChildren();

    for(var n = 0; n < shapes.length; n++) {
        var shape = layer.getChildren()[n];
        setTimeout(function(shape){
            shape.transitionTo({
                alpha: opacity,
                duration: speed
            }); 
        }, 100);                
    }
}
4

5 に答える 5

12

JavaScriptにはブロックスコープがないため、すべてのタイムアウト関数が同じ変数を指しています。この変数shapeは、ループが終了した後、配列の未定義のインデックスを指しています。匿名関数を使用して、探しているスコープをエミュレートできます。

for(var n = 0; n < shapes.length; n++) {
    var shape = shapes[n]; //Use shapes so you aren't invoking the function over and over
    setTimeout((function(s){
        return function() { //rename shape to s in the new scope.
            s.transitionTo({
                alpha: opacity,
                duration: speed
            });
        };
    })(shape), 100);   
}

角かっこを一致させるという私の問題からわかるように、これは少し注意が必要です。これはES5でクリーンアップできますArray.forEach

layer.getChildren().forEach(function(shape) { //each element of the array gets passed individually to the function
    setTimeout(function(shape){
        shape.transitionTo({
            alpha: opacity,
            duration: speed
        }); 
    }, 100);                
});

forEach最新のブラウザに組み込まれていますが、InternetExplorerの古いブラウザではシムすることができます。

于 2012-08-06T00:13:46.410 に答える
1

これは一般的なクロージャーの問題です。修正されたコードは次のとおりです。

function fadeShapes(layer, movement, opacity, speed) {
    var shapes = layer.getChildren();

    for(var n = 0; n < shapes.length; n++) {
        var shape = layer.getChildren()[n];
        setTimeout((function (bound_shape) {
            return function() {  // return function!
                bound_shape.transitionTo({
                    alpha: opacity,
                    duration: speed
                }); 
            };
        }(shape)) // immediate execution and binding
        , 100);                
    }
}

コードで何が起こるかというと、forループが実行され、n関数はでの実行からスケジュールされ100msますが、値はshape変更されます。したがって、コールバックが呼び出されるときは、 (最後の形状)shapeの値です。shapes[length-1]

これを修正するには、クロージャーを使用して値を「閉じる」必要があります。この場合、の値にバインドして、でshape実行する関数を返す関数100ms

于 2012-08-06T00:13:00.820 に答える
0

setTimeout呼び出しを100ms間隔で実行する場合は、各setTimeout呼び出しに100msを追加するだけです。

function fadeShapes(layer, movement, opacity, speed) {
    var shapes = layer.getChildren();

    for(var n = 0; n < shapes.length; n++) {
        var shape = shapes[n];
        setTimeout((function(local_shape){
            return function(){
                local_shape.transitionTo({
                    alpha: opacity,
                    duration: speed
                });
             } 
        })(shape), 100 + n*100);                
    }
}
于 2012-08-06T01:38:59.477 に答える
0

100ms遅延を尊重するイテレータのコードは次のとおりです。(未テスト)

function iterate(array, timeout, callback) {
    var n = 0, length = array.length;
    function step() {
        callback(array[n]);
        n += 1;
        if (n < length) {   // are there more elements?
            setTimeout(step, timeout);
        }
    }
    setTimeout(step, timeout);  // start
}

function fadeShapes(layer, movement /* unused> */, opacity, speed) {
    var shapes = layer.getChildren();
    iterate(shapes, 100, function (shape) {
        shape.transitionTo({
            alpha: opacity,
            duration: speed
        }); 
    });
}

このように関数を使用するiterateことで、クロージャの問題も解決されることに注意してください。

于 2012-08-06T00:28:41.807 に答える
0

試す:

function fadeShapes(layer, movement, opacity, speed) {
    var shapes = layer.getChildren();

    for(var n = 0; n < shapes.length; n++) {
        var shape = layer.getChildren()[n];
        (function(sh) {
            setTimeout(function(){
                sh.transitionTo({
                    alpha: opacity,
                    duration: speed
                }); 
            }, 100); 
        })(shape);
    }
}
于 2012-08-06T03:51:43.670 に答える