2014-12-19 18 views
5

Ho passato molto tempo a cercare di individuare un bug nella mia app. Alla fine ho messo a parte questo pezzo di codice che mi sembra molto strano.Passare alle proprietà in Backbone

var Model = Backbone.Model.extend({ 
    myProperty: [] 
}); 

var one = new Model(); 
var two = new Model(); 
one.myProperty.push(1); 
console.log(two.myProperty); //1!! 

Qual è il motivo? Perché agisce così? Come evitare questo tipo di bug nel codice?

risposta

3

Inheritance in JavaScript è prototipo - oggetti possono rivolgersi direttamente alle proprietà più in alto nella prototype chain.

Nel tuo esempio, one e two entrambi condividono un prototipo comune, e non forniscono i propri valori per myProperty in modo che entrambe si riferiscono direttamente a Model.protoype.myProperty.

È necessario creare un nuovo array myProperty per ciascun modello creato per l'istanza. Model.initialize è la posizione idiomatica per questo tipo di inizializzazione: la sostituzione di constructor è inutilmente complessa.

var Model = Backbone.Model.extend({ 
    initialize: function() { 
     this.myProperty = []; 
    } 
}); 

In alternativa si potrebbe fare myProperty come un attributo del modello:

var Model = Backbone.Model.extend({ 
    defaults: function() { 
     return { 
     myProperty: [] 
     } 
    } 
}); 

E 'importante notare che defaults è una funzione - se si sceglie di usare un semplice oggetto che si verifica lo stesso problema di riferimento condiviso.

+0

Grazie! Ho pensato di inizializzare myProperty in initialize (mi dispiace per tautologia: D), ma mi è sembrato strano che inizializzarlo in un altro modo porta a risultati diversi. Almeno è semanticamente non ovvio ... Ma è un problema javascipt, non Backbone, immagino. – alexb

+1

Esattamente - il punto chiave è che è un JavaScript, non una cosa Backbone. Non è intrinsecamente un problema, infatti è completamente necessario per un sistema prototipo - senza di esso le istanze del modello non possono condividere metodi o proprietà comuni senza creare molti oggetti duplicati. Tuttavia, causa questo tipo di errore molto spesso! – joews

1

La documentazione dice:

costruttore/inizializzare nuovo Modello ([attributi], [opzioni])

Quando si crea un'istanza di un modello, è possibile passare i valori iniziali degli attributi , che sarà impostato sul modello. Se si definisce una funzione di inizializzazione, questa verrà richiamata al momento della creazione del modello.

In rari casi, se si sta cercando di essere fantasiosi, è possibile che si desideri sovrascrivere il costruttore, che consente di sostituire la funzione di costruzione effettiva per il modello.

Così, seguendo la documentazione, che ci si vuole fare qualcosa di simile per ottenere il vostro caso di corsa:

var Model = Backbone.Model.extend({ 
    initialize: function() { 
     this.myProperty = []; 
    } 
}); 

fonte: http://backbonejs.org/#Model-extend

+2

Perché dovresti sovrascrivere 'constructor' per fornire la stessa funzionalità di' initialize'? – Jivings

+1

@Jivings Perché stai incollando dalla pagina di documentazione. Inizializzato '' initialize' nel post – blgt

1

In realtà è perché myProperty è un array, e come saprai, gli array verranno memorizzati come riferimento. Proprio per testare considerare il seguente codice:

var Model = Backbone.Model.extend({ 
myProperty: [], 
messege: '' 
}); 

var one = new Model(); 
var two = new Model(); 

one.messege = 'One!'; 
two.messege = 'Two!'; 

console.log(one.messege); // 'One!' 
console.log(two.messege); // 'Two!' 

Un'alternativa intorno a questo potrebbe essere:

var Model = Backbone.Model.extend({ 
    constructor: function() { 
    this.myProperty = []; 
    Backbone.Model.apply(this); 
    } 
}); 

var one = new Model(); 
one.myProperty.push(1); 

var two = new Model(); 
console.log(two.myProperty); // [] 
+1

È importante sottolineare che la ragione per cui "uno" e "due" condividono un riferimento è l'ereditarietà prototipica - gli oggetti figli condividono un riferimento a una proprietà genitore. – joews

+1

Inoltre, Backbone fornisce 'Model.initialize' per questo tipo di inizializzazione - sovrascrivere' costruttore 'è eccessivo.I documenti Backbone dicono "In rari casi, se stai cercando di essere fantasioso, potresti voler sovrascrivere il costruttore ...". Questo non è un caso raro - è l'esatto caso d'uso comune a cui è destinato l'inizializzazione. – joews

+0

@joews Proprio così, 'initialize' è una scelta migliore in questo caso. – sadrzadehsina

Problemi correlati