Backbone.js Empty Array Attribute

Me encuentro con un problema extraño con un modelo Backbone.js donde un miembro de la matriz se muestra en blanco. Se ve algo como esto:

var Session = Backbone.Model.extend({ defaults: { // ... widgets: [] }, addWidget: function (widget) { var widgets = this.get("widgets"); widgets.push(widget); this.trigger("change:widgets", this, widgets); }, // ... // I have a method on the model to grabbing a member of the array getWidget: function (id) { console.log(this.attributes); console.log(this.attributes.widgets); // ... } }); 

Luego agrego un widget a través de addWidget . Al intentar getWidget el resultado que obtengo (en Chrome) es este:

 Object widgets: Array[1] 0: child length: 1 __proto__: Array[0] __proto__: Object [] 

Está mostrando que los widgets no están vacíos al registrar this.attributes pero se muestra como vacío al iniciar sesión en this.attributes.widgets . ¿Alguien sabe lo que podría causar esto?

EDITAR He cambiado el modelo para crear una instancia de la matriz de widgets en el método de inicialización para evitar referencias en varias instancias, y comencé a usar backbone-nested sin suerte.

Tenga cuidado al confiar en la consola, a menudo hay un comportamiento asincrónico que puede hacer que se tropiece.

Está esperando que console.log(x) comporte de esta manera:

  1. Llama a console.log(x) .
  2. x se descarga a la consola.
  3. La ejecución continúa con la instrucción que sigue inmediatamente a su llamada a console.log(x) .

Pero eso no es lo que sucede, la realidad es más como esta:

  1. Llama a console.log(x) .
  2. El navegador toma una referencia a x y pone en cola la llamada “real” de console.log para más tarde.
  3. Varios otros bits de JavaScript se ejecutan (o no).
  4. Más tarde, la llamada a console.log de (2) consigue deshacerse del estado actual de x en la consola, pero esta x no necesariamente coincidirá con la x como estaba en (2) .

En tu caso, estás haciendo esto:

 console.log(this.attributes); console.log(this.attributes.widgets); 

Entonces tienes algo como esto en (2) :

  attributes.widgets ^ ^ | | console.log -+ | console.log -----------+ 

y luego sucede algo en (3) que efectivamente hace this.attributes.widgets = [...] (es decir, cambia la referencia de attributes.widget ) y entonces, cuando (4) aparece, tienes esto:

  attributes.widgets // the new one from (3) ^ | console.log -+ console.log -----------> widgets // the original from (1) 

Esto te deja viendo dos versiones diferentes de widgets : el nuevo que recibió algo en (3) y el original que está vacío.

Cuando haces esto:

 console.log(_(this.attributes).clone()); console.log(_(this.attributes.widgets).clone()); 

está agarrando copias de this.attributes y this.attributes.widgets que se adjuntan a las llamadas a console.log para que (3) no interfiera con sus referencias y vea resultados razonables en la consola.

Esa es la respuesta a esto:

Está mostrando que los widgets no están vacíos al registrar this.attributes pero se muestra como vacío al iniciar sesión en this.attributes.widgets . ¿Alguien sabe lo que podría causar esto?

En lo que respecta al problema subyacente, es probable que tengas una llamada de fetch alguna parte y no estés teniendo en cuenta su comportamiento asincrónico. La solución probablemente sea enlazar a un evento "add" o "reset" .

Recuerde que [] en JS es solo un alias de la new Array() , y dado que los objetos se pasan por referencia, cada instancia de su modelo de sesión compartirá el mismo objeto de matriz. Esto conduce a todo tipo de problemas, incluidas las matrices que parecen estar vacías.

Para hacer que esto funcione de la manera que desee, debe inicializar su matriz de widgets en el constructor. Esto creará una matriz de widgets única para cada objeto de sesión, y debería aliviar su problema:

 var Session = Backbone.Model.extend({ defaults: { // ... widgets: false }, initialize: function(){ this.set('widgets',[]); }, addWidget: function (widget) { var widgets = this.get("widgets"); widgets.push(widget); this.trigger("change:widgets", this, widgets); }, // ... // I have a method on the model to grabbing a member of the array getWidget: function (id) { console.log(this.attributes); console.log(this.attributes.widgets); // ... } }); 

Probado en un violín con Chrome y Firefox: http://jsfiddle.net/imsky/XBKYZ/

 var s = new Session; s.addWidget({"name":"test"}); s.getWidget() 

Salida de la consola:

 Object widgets: Array[1] __proto__: Object [ Object name: "test" __proto__: Object ]