2011-08-30 8 views
9

Sto scrivendo un codice Actionscript3 che tenta di applicare un metodo a un oggetto determinato in fase di esecuzione. La documentazione AS3 per Function.apply e Function.call indicano che il primo argomento di tali funzioni è l'oggetto che verrà utilizzato come valore "this" quando viene eseguita la funzione.Function.apply not using thisArg parameter

Tuttavia, ho riscontrato che in tutti i casi in cui la funzione eseguita è un metodo, il primo parametro da applicare/chiamare non viene utilizzato e "questo" fa sempre riferimento all'oggetto originale a cui è stato associato quel metodo. Ecco alcuni esempi di codice e la sua uscita:

package 
{ 
    import flash.display.Sprite;  
    public class FunctionApplyTest extends Sprite 
    { 
     public function FunctionApplyTest() 
     { 
      var objA:MyObj = new MyObj("A"); 
      var objB:MyObj = new MyObj("B"); 

      objA.sayName(); 
      objB.sayName(); 

      objA.sayName.apply(objB, []); 
      objA.sayName.call(objB); 
     } 
    } 
} 

internal class MyObj 
{ 
    private var _name:String; 
    public function MyObj(name:String) 
    { 
     _name = name; 
    } 
    public function sayName():void 
    { 
     trace(_name); 
    } 
} 

uscita:

A 
B 
A 
A 

una piccola modifica al codice sopra per creare una funzione in linea anonima che si riferisce a 'questo' mostra che la corretta il comportamento si verifica quando la funzione applicata/chiamata non è un metodo associato.

Sto usando apply/call errato quando tento di usarlo su un metodo? La documentazione AS3 prevede, in particolare il codice per questo caso, però:

myObject.myMethod.call(myOtherObject, 1, 2, 3); 

Se questa è infatti rotto, c'è qualche lavoro-around oltre a rendere i metodi di destinazione in funzioni (che sarebbe abbastanza brutto, a mio parere)?

+0

Devo dire che stai facendo qualcosa di estremamente sbagliato se hai bisogno di questo tipo di codifica. Cosa stai cercando di ottenere esattamente facendo questo? –

+0

Testato da parte mia. sembra un insetto Prova a cercare in JIRA: http://bugs.adobe.com/jira/browse e se non lo trovi, invialo a – divillysausages

+0

non è un bug di lingua, ma documentazione scritta male –

risposta

17

Non è un "bug", ma la documentazione per call e apply è molto fuorviante e non fa un buon lavoro a tutti spiegando che cosa sta succedendo. Quindi ecco una spiegazione di ciò che sta accadendo.

Methods sono diversi da Functions in ActionScript. Methods sono definiti come parte di una definizione di classe e i metodi sono sempre associati a tale istanza. Vedere i metodi secondo di this link. Per quotare da lì:

I metodi sono funzioni che fanno parte di una definizione di classe. Una volta creata un'istanza della classe, un metodo viene associato a tale istanza. A differenza di una funzione dichiarata al di fuori di una classe, un metodo non può essere utilizzato separatamente dall'istanza a cui è collegato.

Quindi, quando si effettua un esempio new di MyObj, tutti i suoi metodi sono tenuti a tale istanza. Questo è il motivo per cui quando si tenta di utilizzare call o apply, non si sta verificando l'override di this. Vedi la sezione su Bound Methods per i dettagli.

Sede, this document per una spiegazione dei tratti oggetto, che ActionScript utilizza per risolvere i metodi e utilizzati per motivi di prestazioni dietro le quinte è probabilmente la colpa. Che o metodi della classe sono lo zucchero sintattico per il seguente schema ECMAScript:

var TestClass = function(data) { 
    var self = this; 
    this.data = data; 
    this.boundWork = function() { 
     return self.constructor.prototype.unboundWork.apply(self, arguments); 
    }; 
}; 

TestClass.prototype.unboundWork = function() { 
    return this.data; 
}; 

Poi:

var a = new TestClass("a"); 
var b = new TestClass("b"); 

alert(a.boundWork()); // a 
alert(b.boundWork()); // b 

alert(a.unboundWork()); // a 
alert(b.unboundWork()); // b 

alert(a.boundWork.call(b)); // a 
alert(a.boundWork.call(undefined)); // a 

alert(a.unboundWork.call(b)); // b 

o anche più interessante:

var method = a.unboundWork; 
method() // undefined. ACK! 

Vs:

method = a.boundWork; 
method() // a. TADA MAGIC! 

Si noti che boundWork verrà sempre eseguito nel contesto dell'istanza a cui appartiene, indipendentemente da cosa si passi per this con call o apply. Che, in ActionScript, questo comportamento è esattamente il motivo per cui i metodi di classe sono legati alla loro istanza. Quindi, indipendentemente da dove vengono utilizzati, puntano ancora all'istanza da cui provengono (il che rende il modello di eventi ActionScript un po 'più "sano di mente"). Una volta capito questo, allora un work-around dovrebbe diventare ovvio.

Per i luoghi in cui si desidera eseguire magie, evitare i metodi con hard-bound basati su ActionScript 3 a favore delle funzioni del prototipo.

Ad esempio, si consideri il seguente codice ActionScript:

package 
{ 
    import flash.display.Sprite;  
    public class FunctionApplyTest extends Sprite 
    { 
     public function FunctionApplyTest() 
     { 
      var objA:MyObj = new MyObj("A"); 
      var objB:MyObj = new MyObj("B"); 

      objA.sayName(); 
      objB.sayName(); 

      objA.sayName.apply(objB, []); // a 
      objA.sayName.call(objB); // a 

      objA.pSayName.call(objB) // b <--- 
     } 
    } 
} 

internal dynamic class MyObj 
{ 
    private var _name:String; 
    public function MyObj(name:String) 
    { 
     _name = name; 
    } 
    public function sayName():void 
    { 
     trace(_name); 
    } 

    prototype.pSayName = function():void { 
     trace(this._name); 
    }; 
} 

notare la differenza tra la dichiarazione di sayName e pSayName. sayName sarà sempre associato all'istanza per cui è stato creato. pSayName è una funzione disponibile per le istanze di MyObj ma non è associata a una particolare istanza di esso.

La documentazione per call e apply sono tecnicamente corretta, a patto che si sta parlando prototipo functions e non di classe methods, che non credo si parla affatto.

+0

Grazie mille per aver spiegato questo, è stato molto illuminante! Penso che probabilmente non è il caso che la dichiarazione del metodo sia zucchero sintattico per il tuo primo esempio di codice, perché se fosse vero allora ClassType.prototype avrebbe delle proprietà su di esso per ciascuno dei metodi non associati. Per me sembra che sarebbe il meglio di entrambi i mondi, in qualche modo simile al meccanismo di Python dove objA.sayName() è zucchero sintattico per MyObj.sayName (objA). –

1

Wow questo è molto sorprendente Oo

Testato sul mio lato come bene e cercato di far passare in parametri come bene e in tutti i casi, il thisArg passato non sembra essere utilizzato a tutti (sembra chiaramente come un bug per me).

Ho dovuto usare qualcosa di simile ma avevo il vincolo aggiuntivo di dover accedere al metodo senza creare un'istanza dell'oggetto (che è possibile in altre lingue ma non in AS3>. <). Così ho finito per creare funzioni statiche e ho invece passato il mio "thisArg".

Così facendo funzioni statiche, invece è una possibile soluzione:

static public function SayName(thisArg : MyObj) : void 
{ 
    trace(thisArg._name); 
} 

Non il più grande dal momento che probabilmente finisce per raddoppiare il codice come questo>. <

In alternativa, se i metodi sono pubblici, è possibile salvare il nome della funzione anziché la funzione ed accedere è il metodo facendo qualcosa di simile:

var funcName : String = "sayName"; 
objB[funcName].apply(null, []); 
objB[funcName].call(null); 

Tuttavia, questa è limitata a seconda della portata del vostro metodo (i metodi pubblici possono essere utilizzati in questo modo da qualsiasi luogo, i metodi interni all'interno dello stesso pacchetto dei metodi di classe e privati ​​solo all'interno della classe). Quindi è più limitante dell'uso dell'istanza Function effettiva del tuo metodo che può essere utilizzata da qualsiasi luogo.

Questo sembra un bug piuttosto brutto O.o Spero che qualcun altro abbia una soluzione migliore a questo.

1

Si è provato a utilizzare effettivamente il riferimento this, ad es.:

internal class MyObj 
{ 
    private var _name:String; 
    public function MyObj(name:String) 
    { 
     _name = name; 
    } 
    public function sayName():void 
    { 
     trace(this._name); 
    } 
} 

Potrebbe essere solo che quando la parola this viene omesso, l'istanza originale viene utilizzato per cercare il campo/variabile, mentre ciò che thisArg fa davvero è quello di ri-legare la parola chiave this. Se è così, nel migliore dei casi è arcano, ma potrebbe valere la pena provare.

+0

+1 'thisArg' fa ri legare 'questo'. Ho trovato i documenti 'Array' per fornire alcuni [esempi simili] (http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/Array.html#filter%28%29) di questo comportamento. – NoobsArePeople2

+0

Sì, ho anche provato a utilizzare la parola chiave "this" in modo esplicito e purtroppo ho riscontrato lo stesso comportamento. La documentazione per Array.filter linkato sopra dice esplicitamente che il parametro 'thisArg' deve essere nullo se la funzione callback è un metodo, quindi sembra che anche l'implementatore del filtro abbia eseguito questo problema. –