Подтвердить что ты не робот

Атрибут пустого массива Backbone.js

Я столкнулся с нечетной проблемой с моделью Backbone.js, где элемент массива отображается как пустой. Это выглядит примерно так:

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);

        // ...
    }
});

Затем я добавляю виджет через addWidget. При попытке getWidget результат, который я получаю (в Chrome), следующий:

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

Показывает, что виджеты не пустые при регистрации this.attributes, но при регистрации this.attributes.widgets он отображается как пустой. Кто-нибудь знает, что может вызвать это?

ИЗМЕНИТЬ Я изменил модель, чтобы создать экземпляр массива виджетов в методе инициализации, чтобы избежать ссылок на несколько экземпляров, и я начал использовать backbone-nested с не повезло.

4b9b3361

Ответ 1

Будьте осторожны, доверяя консоли, часто возникает асинхронное поведение, которое может вас отвлечь.

Вы ожидаете, что console.log(x) будет вести себя следующим образом:

  • Вы вызываете console.log(x).
  • x сбрасывается на консоль.
  • Выполнение продолжается с оператором, сразу после вашего вызова console.log(x).

Но это не то, что происходит, реальность более похожа на это:

  • Вы вызываете console.log(x).
  • Браузер захватывает ссылку на x и отправляет вызов "реальный" console.log для последующего использования.
  • Различные другие биты JavaScript работают (или нет).
  • Позже вызов console.log из (2) обойдется, чтобы сбросить текущее состояние x в консоль, но этот x не обязательно будет соответствовать x как это было в (2).

В вашем случае вы делаете это:

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

Итак, у вас есть что-то вроде этого в (2):

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

а затем что-то происходит в (3), который эффективно выполняет this.attributes.widgets = [...] (т.е. изменяет ссылку attributes.widget), и поэтому, когда (4), у вас есть следующее:

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

В результате вы увидите две разные версии widgets: новую, которая получила что-то в (3) и оригинал, который пуст.

Когда вы это сделаете:

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

вы захватываете копии this.attributes и this.attributes.widgets, которые привязаны к вызовам console.log, поэтому (3) не будет мешать вашим ссылкам, и вы увидите разумные результаты в консоли.

Чтобы ответить на этот вопрос:

Показывает, что виджеты не являются пустыми при регистрации this.attributes, но при регистрации this.attributes.widgets он отображается как пустой. Кто-нибудь знает, что может вызвать это?

Что касается основной проблемы, у вас, вероятно, есть вызов fetch где-то, и вы не учитываете его асинхронное поведение. Возможно, решение связано с событием "add" или "reset".

Ответ 2

Помните, что [] в JS - это просто псевдоним new Array(), и поскольку объекты передаются по ссылке, каждый экземпляр вашей модели сеанса будет использовать один и тот же объект массива. Это приводит к возникновению всех проблем, включая массивы, которые кажутся пустыми.

Чтобы сделать эту работу так, как вы хотите, вам нужно инициализировать ваш массив виджетов в конструкторе. Это создаст уникальный массив виджета для каждого объекта сеанса и позволит устранить вашу проблему:

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);
    // ...
    }
});

Ответ 3

Протестировано в скрипте с Chrome и Firefox: http://jsfiddle.net/imsky/XBKYZ/

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

Выход консоли:

Object
widgets: Array[1]
__proto__: Object

[
Object
name: "test"
__proto__: Object
]