2

以下のトレースが配列項目の「i」の値ではなく配列の長さを返す理由を誰かが説明できるでしょうか。

ニック、ありがとう

AS3

function createMarkers(mapLocations){
    var markerArray:Array = new Array();
    for(i=0; i<mapLocations.length; i++){
        markerArray.push(new marker());
        markerArray[i].x=mapLocations[i][1];
        markerArray[i].y=mapLocations[i][2];

        markerArray[i].markerText.text = mapLocations[i][0].toString();
        markerArray[i].addEventListener(MouseEvent.CLICK, function(e:MouseEvent){clickTarget(e,i);});
        bgImage.addChild(markerArray[i]);
    }
}

function clickTarget(e:MouseEvent,a){
    trace(a);
}
4

3 に答える 3

4

これは、JavaScript/ActionScript関数を扱うときによくある間違いです。関数はクロージャであるため、問題が発生しています。つまり、関数が定義されたときにスコープで定義された変数への参照を保持します。

これは、匿名ハンドラー関数が変数の周囲を閉じますがi、値ではなく変数への参照を格納することを意味します。変更されるためi、すべての関数は同じ変数への参照を保持し、最後に割り当てられた値を保持します。

基本的に、変数の特定の値を閉じたい場合は、関数のスコープ内で(ステートメントを使用して)変数を宣言する必要があります。varそのため、これは機能するはずです。

for (var i:int = 0; i < 10; i++) {
    var scopedI:int = i;
    mc[i].addEventListener(MouseEvent.CLICK, function (e:MouseEvent) { trace(scopedI); });
}

ループのスコープ内で新しい変数を宣言して、scopedIその値を具体的に閉じます。これは、その変数がループの反復ごとに一意の値で再宣言されるためです。残念ながら、JavaScriptと同様に、ActionScriptにはブロックレベルのスコープがなく、関数レベルのスコープしかないため、すべての変数宣言は関数の先頭に「持ち上げられ」ます。

これは、タイプが、、およびその関数内で宣言された他の変数とscopedI同じスコープを持っていることを意味します。iでは、どうすれば新しいスコープを作成できますか?より多くの機能を備えています。ActionScriptでは、関数はオブジェクトであるため、次のようなクレイジーなことを実行できることに注意してください。

(function (id) {
    return function () { trace(id); };
})(7);

そのコードのビットは関数を作成し、すぐに7パラメーターの値を使用して関数を実行しidます。これは便利です。これは、id返される内部関数にスコープが設定されているため、外部で何が起こっても、関数は常に「7」を出力します。

同様に、これを使用iして、ループ内の変数のスコープを設定できます。コードを次のように更新できます。

function createMarkers(mapLocations){
    var markerArray:Array = new Array();
    for(var i = 0; i < mapLocations.length; i++){
        markerArray.push(new marker());
        markerArray[i].x = mapLocations[i][1];
        markerArray[i].y = mapLocations[i][2];

        markerArray[i].markerText.text = mapLocations[i][0].toString();
        markerArray[i].addEventListener(MouseEvent.CLICK, (function (scopedI) {
                return function (e:MouseEvent) { clickTarget(e, scopedI); };
            })(i));
        bgImage.addChild(markerArray[i]);
    }
}

function clickTarget(e:MouseEvent, a){
    trace(a);
}

これで、scopedI反復ごとに一意になります。はい、構文は少し厄介ですが、これは言語の非常に強力で表現力豊かな機能になります。あなたがそれを理解することができれば、それは非常に役に立ちます。

于 2012-12-24T22:55:01.097 に答える
1

アレクシスが言ったことに加えて、

Clickイベントを追加しているので、アイテムは表示オブジェクトであると想定しています。

Javascriptとは異なり、次のように、アイテムに名前を付けてハンドラーに戻すこともできます。

        ...
        markerArray[i].name = "marker" + i; 
        markerArray[i].addEventListener(MouseEvent.CLICK, clickTarget);
        bgImage.addChild(markerArray[i]);
    }
}

function clickTarget(e:MouseEvent){
    var a = e.currentTarget.name.substr(6);
    trace(a);
}

もちろん、これまでname属性が役に立たなかったと仮定することもできます。

于 2012-12-25T04:04:50.540 に答える
-1

Because your inline mouse event is capturing the variable i (a reference) instead of its value.

Try this:

function createMarkers(mapLocations){
    var markerArray:Array = new Array();
    for(var i:Number = 0; i<mapLocations.length; i++){
        var thisI:Number = i;
        markerArray.push(new marker());
        markerArray[thisI].x=mapLocations[thisI][1];
        markerArray[thisI].y=mapLocations[thisI][2];

        markerArray[thisI].markerText.text = mapLocations[thisI][0].toString();
        markerArray[thisI].addEventListener(MouseEvent.CLICK,
            function(e:MouseEvent){
                clickTarget(e,thisI);
            });
        bgImage.addChild(markerArray[thisI]);
    }
}

This should work because thisI is being recreated with each iteration of the loop.

于 2012-12-24T21:46:45.320 に答える