2012-04-02 16 views
6

Sto provando a risolvere questo problema OOP JavaScript Javascript.Classi Javascript e riferimenti variabili

così ho la seguente classe:

var ClassA = function() { 
    this.initialize(); 
} 

ClassA.prototype = { 

    methods : ['alpha','beta','gama'], 

    initialize : function() { 
     for (var i in this.methods) { 
      this[this.methods[i]] = function() { 
       console.log(this.methods[i]); 
      } 
     } 
    } 
} 

var a = new ClassA(); 

Quando chiamo ogni metodo mi aspetto di stampare il nome di essa, giusto? Ma qui è ciò che ottengo:

a.alpha(); // returns gama ?!? 
a.beta(); // returns gama ?!? 
a.gama(); // returns gama 

Ma quando la mia classe è simile al seguente:

var ClassB = function() { 
    this.initialize(); 
} 

ClassB.prototype = { 

    methods : ['alpha', 'beta', 'gama'], 

    initialize: function() { 
     for (var i in this.methods) { 
      this.addMethod(this.methods[i]); 
     } 
    }, 

    addMethod: function(method) { 
     this[method] = function() { 
      console.log(method); 
     } 
    } 

} 

var b = new ClassB(); 

b.alpha(); // returns alpha 
b.beta(); // returns beta 
b.gama(); // returns gama 

perché sta succedendo questo?

+0

Non è [il modo errato] (http://stackoverflow.com/questions/3010840/loop-through-array-in-javascript#answer-3010848) di eseguire il looping di un array in JS? – PeeHaa

+0

@RepWhoringPeeHaa - Sì, dovrebbe essere usato un ciclo for for, ma non è questo il problema. – nnnnnn

+0

@nnnnnn So che è per questo che è un commento e non una risposta :-) – PeeHaa

risposta

6
for (var i in this.methods) { 
     this[this.methods[i]] = function() { 
      console.log(this.methods[i]); 
     } 
} 

Il tuo problema si trova qui. Al termine di questo ciclo, i è l'ultimo elemento. Ogni funzione utilizza lo stesso i, quindi sono tutti gli ultimi elementi.

Quando si utilizza addMethod si sta effettuando una chiusura per "acquisire" il valore corretto.

MODIFICA: quando si chiama addMethod si "copia" il valore, invece di utilizzare il valore i, che cambia ad ogni iterazione del ciclo.

+0

Rispondo a una domanda come questa quasi ogni giorno ... http://stackoverflow.com/questions/9980209/register-onclick-events-from -dynamically-created-div-array-rails-jquery/9980579 # 9980579 –

+0

Sono confuso ... Non è console.log solo un'altra funzione, e da addMethod lo sto semplicemente avvolgendo in un altro? – drinchev

+0

@drinchev: 'console.log' non è importante qui. Ciò che è importante qui, è che quando chiami 'addMethod', stai copiando il valore in ogni funzione, ma quando lo fai senza di esso, stai usando lo stesso valore. (Scusa se non lo sto spiegando bene.) –

3

Nella prima versione:

initialize : function() { 
    for (var i in this.methods) { 
     this[this.methods[i]] = function() { 
      console.log(this.methods[i]); 
     } 
    } 
} 

I metodi che si crea all'interno initialize tutto si riferiscono alla stessa variabile i da initialize - e dopo initialize corre i ha il valore "gama", quindi indipendentemente da quale dei metodi tu chiami che è il valore di i che accederanno alla console. JS non memorizza il valore corrente di i al momento della creazione del metodo.

JS crea una "chiusura" per ogni funzione - le variabili dichiarate nella funzione initialize (vale a dire, i) continuano ad essere portata per la funzione annidata (s) anche dopo initialize ha finito.

La seconda versione chiamate addMethod aggiungere ogni metodo:

addMethod: function(method) { 
    this[method] = function() { 
     console.log(method); 
    } 
} 

... e così quando corrono faranno riferiscono alla propria "copia" del parametro method perché poi c'è una chiusura separata per ciascuno dei metodi.

Modifica: Vedi anche questa domanda: How do JavaScript closures work? (diverse risposte spiegano questo più chiaramente di quanto ho fatto).

1

È possibile risolvere il tuo primo esempio con l'aggiunta di una chiusura anonima:

initialize : function() { 
    for (var i in this.methods) { 
     (function (i) { // anonymous closure 
      this[this.methods[i]] = function() { 
       console.log(this.methods[i]); 
      } 
     }).call(this, i); // use .call() if you need "this" inside 
    } 
} 

Ora che funzionerà allo stesso modo il secondo esempio. "Anonimo" significa che la chiusura è fatta da una funzione che non ha un nome e viene chiamata istantaneamente poiché è "creata".

Nota di lato: utilizzare .call(this, ...) per preservare this all'interno della funzione chiamata, o si può fare var that = this, utilizzare that invece di this e chiama la funzione normalmente:

for (var i in this.methods) { 
    var that = this; 
    (function (i) { // anonymous closure 
     that[that.methods[i]] = function() { 
      console.log(that.methods[i]); 
     } 
    })(i); // Called normally so use "that" instead of "this"! 
} 
+1

Ha già risolto il problema, voleva sapere perché era un problema in primo luogo. –

+0

Sì, ma questo modo di risolverlo è più semplice, più semplice. – TMS

+0

Sono d'accordo, ma continua a non rispondere alla domanda originale. –

0

Beh, prima di tutto smettere di usare per (proprietà nell'oggetto) loop su Arrays. È tutto divertimento e giochi fino a quando qualcuno non prototipa sull'oggetto Array, che è una cosa perfettamente ragionevole e molto utile/popolare da fare. Ciò comporterà l'aggiunta di metodi personalizzati per x nei loop di array.

Per quanto riguarda il problema, sta facendo esattamente quello che hai detto di fare nella versione 1. Il problema è che quando arrivi a sparare, io sono l'ultima cosa che ero, 'gamma'. Quando si passa un riferimento in una funzione come argomento, la funzione mantiene lo stato del valore nel momento in cui è stato passato.

Problemi correlati