2009-01-08 12 views
25

In dem folgenden Code versuche ich, einige Bilder zu laden und sie in die Bühne, sobald sie einzeln geladen werden. Aber es ist abgehört, da nur das letzte Bild angezeigt wird. Ich vermute, dass es ein Schließungsproblem ist. Wie kann ich es reparieren? Ist das Verhalten von Closures in AS3 nicht dasselbe wie in Java Script?So beheben Sie das Schließen-Problem in ActionScript 3 (AS3)

var imageList:Array = new Array(); 
imageList.push({'src':'image1.jpg'}); 
imageList.push({'src':'image2.jpg'}); 
var imagePanel:MovieClip = new MovieClip(); 
this.addChildAt(imagePanel, 0); 

for (var i in imageList) { 
    var imageData = imageList[i]; 
    imageData.loader = new Loader(); 

    imageData.loader.contentLoaderInfo.addEventListener(
     Event.COMPLETE, 
     function() { 
      imagePanel.addChild(imageData.loader.content as Bitmap); 
      trace('Completed: ' + imageData.src);    
     }); 

    trace('Starting: ' + imageData.src); 
    imageData.loader.load(new URLRequest(imageData.src)); 
} 
+0

Ich würde nicht vorschlagen, die Javascript-Tag verwenden, wenn es nicht viel Relevanz hat, also habe ich es entfernt. – Salty

Antwort

45

Ist das nicht das Verhalten von Verschlüssen in AS3 die gleiche wie in Java Script?

Ja, JavaScript macht genau dasselbe. Wie auch Python. Und andere.

Obwohl Sie 'var imageData' innerhalb des 'for' definieren, führen for-Schleifen in diesen Sprachen keinen neuen Bereich ein; Tatsächlich ist die Variable imageData im enthaltenden Bereich gebunden (die äußere Funktion, oder in diesem Fall scheint sie globaler Geltungsbereich zu sein). Sie können dies überprüfen, indem Sie imageData betrachten, nachdem die Schleife vollständig ausgeführt wurde, und das letzte Element von imageList darin finden.

Es gibt also nur eine imageData-Variable, nicht eine für jede Iteration der Schleife. Wenn COMPLETE ausgelöst wird, gibt es den Abschluss ein und liest den Wert imageData jetzt nicht zum Zeitpunkt der Definition der Funktion (*). In der Regel endet die FOR-Schleife mit dem Punkt COMPLETE und imageData hält das letzte Element der letzten Iteration.

(* - es existieren ‚early-binding‘ Sprachen, die wird den Wert der Variablen an der Stelle bewerten definieren Sie eine Schließung Aber Actionscript ist nicht einer von ihnen..)

Mögliche Lösungen einzubinden neigen mit eine äußere Funktion, um einen neuen Bereich einzuführen. Zum Beispiel:

function makeCallback(imageData) { return function() { 
    imagePanel.addChild(imageData.loader.content as Bitmap); 
    trace('Completed: ' + imageData.src);                          
} } 
... 
imageData.loader.contentLoaderInfo.addEventListener(Event.COMPLETE, makeCallback(imageData)); 

Sie/kann/diese inline setzen, aber die doppelt verschachtelte Funktion() beginnt zu bekommen schwieriger zu lesen.

Siehe auch Function.bind() für eine allgemeine Teilfunktionsfunktion, die Sie verwenden könnten, um dies zu erreichen. Es wird wahrscheinlich Teil zukünftiger JavaScript/ActionScript-Versionen sein und kann in der Zwischenzeit durch Prototyping zur Sprache hinzugefügt werden.

+0

Danke für die Klärung der Dinge. In der Tat unterscheidet sich das Verhalten von Java Script, da Java Script eine "früh bindende" Sprache ist, die meiner Meinung nach besser ist. Warum würde man das AS3-Verhalten bevorzugen? – fromvega

+0

Nein, ActionScript und JavaScript verhalten sich gleich - sie müssen, da sie beide dem ECMAScript-Standard entsprechen. Es gibt auch keine frühbindenden Sprachen, die eher in der funktionalen Programmierung auftauchen. Ich würde gerne eine früh verbindliche Sprache sehen, die auf einer modernen Skriptsprache basiert. – bobince

+0

Sie haben Recht! Ich mache einige Tests und tatsächlich ist das Verhalten das gleiche. Ich denke, ich dachte, es wäre anders, weil ich nie mit einer solchen Verzögerung in Javascript konfrontiert werde. Vielen Dank! – fromvega

1
(function() { 
    var imageData = imageList[i]; 
    imageData.loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function() { 
    // use imageData; 
    }); 
}).apply(); 
2

Verwenden Sie den mehr funktionalen Stil für jede Methode auf der Array-Klasse, dieses Problem zu vermeiden. Dies wurde bereits erwähnt, aber ich werde es hier weiter ausführen.

imageList.forEach(function (item:MovieClip, index:int, list:Array) { 
    // add your listener with closure here 
}) 

Mit dieser Methode wird die Funktion in die forEach geleitet definiert einen neuen Bereich jeder Iteration. Jetzt können Sie einen Abschluss in diesem Bereich hinzufügen und er wird sich an jede Instanz erinnern, wie Sie möchten.

Über einen entsprechenden Hinweis:

diejenigen 3 Argumente die ganze Zeit so ein Schmerz Typing ist ... Sie können auch, dass weniger/mehr hässlich mit einem Adapter Funktion:

// just give me the item please 
imageList.forEach (itrAdpt(function (image: ImageData) { 
    // add your listener with closure here 
})) 

// give me the item and it's index 
imageList.forEach (itrAdpt(function (image: ImageData, index:int) { 
    // add your listener with closure here 
})) 

// give me the item, index and total list length 
imageList.forEach (itrAdpt(function (image: ImageData, index:int, length:int) { 
    // add your listener with closure here 
})) 

wo itrAdpt ist eine (möglicherweise global) Funktion wie etwas definiert:

public function itrAdpt(f: Function): Function 
{ 
    var argAmount:int = f.length 

    if (argAmount == 0) 
    { 
     return function (element:*, index:int, colection:*):* { 
      return f(element) 
     } 
    } 
    else if (argAmount == 1) 
    { 
     return function (element:*, index:int, colection:*):* { 
      return f(element) 
     } 
    } 
    else if (argAmount == 2) 
    { 
     return function (element:*, index:int, colection:*):* { 
      return f(element, index) 
     } 
    } 
    else if (argAmount == 3) 
    { 
     return function (element:*, index:int, colection:*):* { 
      return f(element, index, colection.length) 
     } 
    } 
    else 
    { 
     throw new Error("Don't know what to do with "+argAmount+"arguments. Supplied function should have between 1-3 arguments") 
    } 
} 
1

Wenn eine benannte Funktion Erstellung nicht appelliere an Sie, bobince Antwort kann ohne viel Opfer, um die Lesbarkeit zu dieser umgewandelt werden:

var makeCallback = function(imageData:String) 
    { 
    return function(evt:Event) 
    { 
     imagePanel.addChild(imageData.loader.content as Bitmap); 
     trace('Completed: ' + imageData.src); 
    } 
    } 

... 

imageData.loader.contentLoaderInfo.addEventListener(Event.COMPLETE, makeCallback(imageData)); 

Nur meine Präferenz, die Leistung kann variieren.