2010-12-13 12 views
19

Qualcuno ha notato questo comportamento prima? Questo mi ha davvero sconvolto ... mi sarei aspettato che gli array prototipati fossero privati ​​per ogni istanza di classe piuttosto che condivisi tra tutte le istanze di classe.I membri di oggetti Javascript che vengono prototipati come matrici vengono condivisi da tutte le istanze di classe

Qualcuno può verificare che questo sia il comportamento corretto e forse spiegare questo comportamento in modo più dettagliato?

Si noti il ​​codice commentato e il modo in cui influisce sul comportamento dello script.

<html> 
<head> 

<script type="text/javascript"> 

function print_r(title, object) { 

    var output = ''; 
    for(var key in object) { 

     output += key + ": " + object[ key ] + "\n"; 

    } 

    output = title + "\n\n" + output; 

    alert(output); 

} 

function Sandwich() { 

    // Uncomment this to fix the problem 
    //this.ingredients = []; 

} 

Sandwich.prototype = { 

    "ingredients" : [], 
    "addIngredients" : function(ingArray) { 

     for(var key in ingArray) { 

      this.addIngredient(ingArray[ key ]); 

     } 

    }, 
    "addIngredient" : function(thing) { 

     this.ingredients.push(thing); 

    } 

} 

var cheeseburger = new Sandwich(); 
cheeseburger.addIngredients([ "burger", "cheese" ]); 

var blt = new Sandwich(); 
blt.addIngredients([ "bacon", "lettuce", "tomato" ]); 

var spicy_chicken_sandwich = new Sandwich(); 
spicy_chicken_sandwich.addIngredients([ "spicy chicken pattie", "lettuce", "tomato", "honey dijon mayo", "love" ]); 

var onLoad = function() { 

    print_r("Cheeseburger contains:", cheeseburger.ingredients); 

}; 

</script> 

</head> 
<body onload="onLoad();"> 
</body> 
</html> 

Molte grazie.

risposta

31

Il prototipo di un oggetto è solo un oggetto. Le proprietà del prototipo sono condivise tra tutti gli oggetti che ereditano da tale oggetto. Nessuna copia delle proprietà viene creata se si crea una nuova istanza di una "classe" (le classi non esistono comunque in JS), cioè un oggetto che eredita dal prototipo.

fa solo una differenza su come si utilizzano i queste proprietà ereditate:

function Foo() {} 

Foo.prototype = { 
    array: [], 
    func: function() {} 
} 

a = new Foo(); 
b = new Foo(); 

a.array.push('bar'); 
console.log(b.array); // prints ["bar"] 

b.func.bar = 'baz'; 
console.log(a.func.bar); // prints baz 

In tutti questi casi si è sempre a lavorare con lo stesso oggetto.

Ma se assegna un valore ad una proprietà dell'oggetto, la proprietà verrà impostata/creato sull'oggetto stesso, non il suo prototipo, e quindi non è condivisa:

console.log(a.hasOwnProperty('array')); // prints false 
console.log(a.array); // prints ["bar"] 
a.array = ['foo']; 
console.log(a.hasOwnProperty('array')); // prints true 
console.log(a.array); // prints ["foo"] 
console.log(b.array); // prints ["bar"] 

Se si desidera creare propri array per ogni istanza, è necessario definire nel costruttore:

function Foo() { 
    this.array = []; 
} 

perché qui, this si riferisce all'oggetto new che viene generato quando si chiama new Foo().

La regola generale è: grado dati specifico d'devono essere assegnati al esempio all'interno costruttore, condivisi dati (come metodi) dovrebbe essere assegnato alla prototipo.


Si potrebbe desiderare di leggere Details of the object model che descrive le differenze tra classe a base vs. lingue prototipo-based e come gli oggetti effettivamente funzionare.

Aggiornamento:

È possibile accedere al prototipo di un oggetto tramite Object.getPrototypeOf(obj) (potrebbe non funzionare molto vecchi browser), e Object.getPrototypeOf(a) === Object.getPrototypeOf(b) ti dà true. È lo stesso oggetto, noto anche come Foo.prototype.

+0

Hmm ... Sì, questo ha senso. Ho fatto l'errore di aspettarmi che il prototipo funzionasse come farebbe una definizione di classe. La cosa confusa è che se Foo.prototype.intValue = 5, e dici b.intValue = 4, a.intValue still = 5. Quindi si applica solo ai membri dell'oggetto/array della classe. – Dan

+0

Poi di nuovo, se dici b.array = ['whatever'], allora a.array non cambia neanche. Pertanto, le assegnazioni esplicite che utilizzano l'operatore "=" sovrascrivono le proprietà prototipate su un oggetto istanziato. – Dan

+0

@Dan: No, ho anche mostrato nel mio codice che 'a.array = ['foo'];' non cambierà il prototipo. Ma nel tuo caso con il numero intero e nel mio caso con l'array, stai assegnando un valore totalmente nuovo a una proprietà. E se lo fai, la proprietà sarà impostata sull'oggetto reale. Ma se si esegue 'a.array.push (1)', si ** non ** si imposta una proprietà, si sta semplicemente chiamando un metodo nella matrice contenuta in 'a.array'. Non stai cambiando il riferimento. –

1

Il comportamento è corretto. [] viene tranciato a new Array() in runtime, ma viene creato solo uno di questi array.

In altre parole, Obj.prototype = {...} viene eseguito come qualsiasi altro assegment.

-1

Quando si esegue var exp1 = new C(), JavaScript imposta exp1.[[Prototype]] = C.prototype. Quando si accede alle proprietà dell'istanza, JavaScript controlla innanzitutto se esistono su quell'oggetto direttamente, e in caso contrario, appare in [[Prototype]]. Ciò significa che tutte le cose che definisci in prototipo sono effettivamente condivise da tutte le istanze, e puoi anche modificare in seguito parti del prototipo e far apparire le modifiche in tutte le istanze esistenti.

Problemi correlati