2013-03-24 9 views
5

dire che ho una classe e alcuni metodi di supporto statici come questo:oggetto Assegna a "questo"

function MyClass (myVar) { 
    this.myVar = myVar; 

    this.replaceMe = function (value) { 
     // this will fail 
     this = MyClass.staticHelper(value); 

     return this; 
    } 

    this.revealVar = function() { 
     alert(this.myVar); 
    } 
} 

MyClass.staticHelper = function (instance, value) { 
    return new MyClass(instance.myVar + value); 
} 

Quello che voglio fare è qualcosa di simile:

var instance = new MyClass(2); 

instance.revealVar(); // alerts 2 
instance.replaceMe(40).revealVar(); // alerts 42 

La ragione è che la mia classe ha una struttura leggermente più complicata e non voglio assegnare manualmente tutte le variabili interne ogni volta, ma piuttosto sostituire l'intero oggetto. C'è un modo semplice per farlo?

+2

Sembra che tu voglia solo restituire MyClass.staticHelper (this, value); '. Non puoi sostituire "questo", ma puoi restituire un nuovo oggetto simile. –

+0

Questo restituirà solo il nuovo oggetto all'interno di una catena di chiamate, ma in realtà non sostituirà l'istanza. –

+0

Sì, non è possibile. Puoi creare un wrapper per l'intera classe e mantenere comunque un riferimento interno all'istanza attuale. –

risposta

0

Come circa appena tornare la nuova istanza:

function MyClass(myVar) { 
    // ... 

    this.replaceMe = function (value) { 
     return MyClass.staticHelper(this, value); 
    } 

    // ... 
} 

MyClass.staticHelper = function (instance, value) { 
    return new MyClass(instance.myVar += value); 
} 
+0

Come menzionato nei commenti della domanda, questo in realtà non sostituirà l'istanza, cioè al di fuori di una catena di chiamate l'oggetto non sarà toccato. –

+0

@ IngoBürk Ho anche aggiunto '+ =' invece di '=' in modo da mutare la proprietà dell'oggetto. – 0x499602D2

+0

Oh, non l'ho visto. Ma questo sconfigge lo scopo della mia domanda: la mia classe ha bisogno di un aggiornamento su diverse variabili su un cambiamento. Stavo cercando un modo per non doverlo fare manualmente. –

0

Ci sono due ragioni per cui questo non è andare a lavorare in Javascript.

Innanzitutto, nonostante assomigli a una variabile, this è in realtà una chiamata di funzione * e pertanto non può essere assegnata a. this=foo corrisponde a bar()=baz. Quindi non è possibile avere codice come questo:

a = 5 
a.change(10) 
alert(a == 10) // nope 

In secondo luogo, anche se this=z fosse possibile, che l'approccio fallirebbe in ogni modo, perché Javascript passa per valore, quindi non è possibile avere una funzione che modifica il valore di il suo argomento:

a = 5 
change(a) 
alert(a == 10) // nope 

* "è" significa "tutto identico in ogni modo"

+0

Come pensi che "questa" sarebbe una chiamata di funzione ??? – Bergi

+3

La specifica non dice che si tratta di una chiamata di funzione. [Ha solo menzionato 'ThisBinding'] (http://es5.github.com/#x10.4.3) che è descritto come *" Il valore associato alla parola chiave 'this'" all'interno del codice ECMAScript associato a questo contesto di esecuzione. "* (http://es5.github.com/#x10.3, http://es5.github.com/#x11.1.1). –

+0

Il puntatore "this'" all'interno di una funzione è un valore di sola lettura, non una chiamata di funzione. Questo è il motivo per cui le assegnazioni a "questo" non funzionano. –

1

instance.replaceMe(40).revealVar(); avvisi 42

OK, per che return MyClass.staticHelper(this, value); sarebbe sufficiente. La questione è solo se la chiamata successiva a instance.revealVar() dovrebbe avvisare 2 o 42 - se si vuole instance di essere cambiato a 42 diventa più complicato:

this = MyClass.staticHelper(value); // this will fail 

... perché this non è una variabile comune, ma un parola chiave e evaluates to the value of the ThisBinding of the current execution context che is set depending on how the function is entered - non è possibile assegnarlo, è possibile impostarlo solo quando si richiama la funzione.

Non voglio assegnare manualmente tutte le variabili interne ogni volta, ma piuttosto sostituire l'intero oggetto.

Purtroppo c'è un devi fare così, senza modificare le proprietà di instance oggetto (e le variabili di chiusura nascosto) di non modificare la instance e revealVar() rimarrà 2.

Is modo semplice per farlo?

Sì, può essere fatto a livello di programmazione.Il metodo più semplice sarebbe quella di call il costruttore (di nuovo) sul l'istanza corrente, come accade quando invocato con l'new keyword:

MyClass.call(instance, instance.myVar + value); 

Eppure non è possibile utilizzare questo come la funzione statica che crea un completamente nuova istanza. O lo si inserisce in un metodo statico e lo si chiama da replaceMe con this oppure lo si inserisce direttamente in replaceMe.

Se avete bisogno di un metodo statico che in un primo momento restituisce una completamente nuova istanza, si potrebbe usare anche quello copiando le nuove proprietà sul vecchio esempio:

….replaceMe = function(val) { 
    var newInst = MyClass.staticHelper(this, val); // new MyClass(this.myVar+val); 
    for (var prop in newInst) 
     if (newInst.hasOwnProperty(prop)) 
      this[prop] = newInst[prop]; 
    return this; 
}; 

Ciò significa sovrascrivendo i vecchi attributi, e anche le vecchie chiusure possono essere raccolte dai rifiuti ora che nulla si riferisce più a loro.

Btw, mi consiglia di put your methods on the prototype instead of assigning them in the constructor.

0

Volevo fare qualcosa di molto simile qualche tempo fa. Sfortunatamente non c'è modo di assegnare un valore a this - il puntatore this è una variabile di sola lettura. Comunque la cosa migliore da fare è usare un oggetto getter e setter per cambiare la variabile tenendo la tua istanza stessa.

Si noti che questo aggiorna solo un singolo riferimento all'istanza. Si può leggere di più qui: Is there a better way to simulate pointers in JavaScript?

Quindi questo è come funziona:

function MyClass(pointer, myVar) { 
    this.myVar = myVar; 

    this.replaceMe = function (value) { 
     pointer.value = MyClass.staticHelper(this, pointer, value); 
     return pointer.value; 
    }; 

    this.revealVar = function() { 
     alert(this.myVar); 
    }; 
} 

MyClass.staticHelper = function (instance, pointer, value) { 
    return new MyClass(pointer, instance.myVar + value); 
}; 

Questo è il modo per creare il pointer e usarlo:

var instance = new MyClass({ 
    get value() { return instance; }, 
    set value(newValue) { instance = newValue; } 
}, 2); 

instance.revealVar();    // alerts 2 
instance.replaceMe(40).revealVar(); // alerts 42 

Non è il più elegante soluzione ma fa il lavoro. Puoi vedere questo codice in azione: http://jsfiddle.net/fpxXL/1/

Problemi correlati