2012-06-12 13 views
5

Ho alcuni problemi con JavaScript. Ho il seguente codice:JavaScript: l'aggiunta di una classe ereditata all'array non funziona

<html> 
<head> 
<title>Test</title> 
<script type="text/javascript"> 
function Control(){ 
    var name; 

    this.setName = function(newName){ 
     name = newName; 
    }; 

    this.getName = function(){ 
     return name; 
    }; 
} 

function SpecializedControl(){ 
} 
SpecializedControl.prototype = new Control(); 

function Form(){ 
    var formControls = []; 

    this.addControl = function(control){ 
     formControls.push(control); 

     alert(formControls[0].getName()); 
    }; 
} 

var form = new Form(); 

var control1 = new SpecializedControl(); 
control1.setName("Control1"); 
form.addControl(control1); 

var control2 = new SpecializedControl(); 
control2.setName("Control2"); 
form.addControl(control2); 

</script> 
</head> 
<body> 
</body> 
</html> 

Il controllo Specialized eredita dalla classe Control.

La funzione addControl nella classe Form aggiunge semplicemente il controllo a un array.

Il problema è che quando aggiungo più di uno SpecializedControl, i valori nell'array sono sovrascritti, ciò significa che quando accedo al primo elemento dell'array, che dovrebbe essere "Control1", ottengo "Control2" . Control1 non è più nell'array.

Quando utilizzo la stessa funzione con oggetti Control come parametri, tutto funziona come previsto.

Qualcuno sa perché questo accade e cosa si può fare per correggere questo?

risposta

5

I valori nella matrice non vengono sovrascritti ; il problema è che entrambi i controlli condividono la stessa variabile name. Poiché la funzione Control viene eseguita solo una volta, è stata dichiarata una sola variabile name.

Hai due opzioni principali per risolvere questo problema. (1) Creare name una variabile di istanza specifica per ogni singolo controllo (ad esempio this._name). (2) Eseguire la funzione Control dall'interno del costruttore SpecializedControl. (In realtà, IMO, per un modello di ereditarietà ben bilanciato e completo dovresti fare un po 'di entrambi questi metodi).

Ecco tre soluzioni di lavoro. Le prime due usano rispettivamente le opzioni (1) e (2). Il terzo combina entrambi i metodi ed è il modo in cui lo farei (ma richiede joi).

Opzione 1:

function Control(){ 

    this.setName = function(newName){ 
     this._name = newName; 
    }; 

    this.getName = function(){ 
     return this._name; 
    }; 
} 

Opzione 2:

function SpecializedControl(){ 
    Control.apply(this, arguments); 
} 

Opzione 3:

var Control = joi.Unit.sub(function() { 

    function constructor() { 
     this.base(); 
    } 

    constructor.prototype = { 
     '#name': null, 
     setName: function(name) { 
      this['#name'] = name; 
     }, 
     getName: function() { 
      return this['#name']; 
     } 
    }; 

    return constructor; 

}()); 

var SpecializedControl = Control.sub(function() { 

    function constructor() { 
     this.base(); 
    } 

    return constructor; 

}()); 

var Form = joi.Unit.sub(function() { 

    function constructor() { 
     this.base(); 
     this['#formControls'] = []; 
    } 

    constructor.prototype = { 
     '#formControls': null, 
     addControl: function(control) { 
      this['#formControls'].push(control); 
      alert(this['#formControls'][0].getName()); 
     } 
    }; 

    return constructor; 

}()); 

var form = new Form(); 

var control1 = new SpecializedControl(); 
control1.setName("Control1"); 
form.addControl(control1); 

var control2 = new SpecializedControl(); 
control2.setName("Control2"); 
form.addControl(control2);​ 
+1

vi consiglio la seconda opzione, chiamando il costruttore della superclasse nel costruttore della sottoclasse. Sembra la cosa giusta da fare. La prima opzione richiede di esporre la variabile 'name' e la terza opzione utilizza una libreria aggiuntiva che tenta di implementare un'eredità regolare basata sulla classe come sostituzione dell'eredità prototipica di JavaScript. –

+0

joi utilizza l'ereditarietà prototipica, non l'ereditarietà classica –

+0

Oh, mio ​​male. Tuttavia, il '# nome' è solo per convenzione e in realtà non ti impedisce di accedervi dall'esterno. Con chiusure, puoi avere variabili veramente private. –

5

Le tue get/setName funzioni stanno ottenendo/impostando il valore della variabile name locale alla funzione di costruttore Control.

L'unica volta in cui è stata invocata quella funzione, era quella di creare un'istanza come l'oggetto prototype di SpecializedControl. Pertanto, ogni volta che hai invocato setName da un'istanza SpecializedControl, quella variabile singola viene aggiornata.

Quindi, poiché i metodi get/setName che fanno riferimento a tale variabile sono nella catena di prototipi di tutte le istanze SpecializedControl, osserveranno tutti lo stesso name.


In setName, si dovrebbe fare ...

this.name = newName; 

E in getName, si dovrebbe fare ...

return this.name; 
Problemi correlati