13

我在 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。有谁知道这会导致什么?

编辑 我已经更改了模型以在初始化方法中实例化小部件数组以避免跨多个实例的引用,并且我开始使用骨干嵌套但没有运气。

4

3 回答 3

23

小心信任控制台,经常有异步行为会让你绊倒。

你期望console.log(x)表现得像这样:

  1. 你打电话console.log(x)
  2. x被转储到控制台。
  3. console.log(x)在您调用之后立即执行语句继续执行。

但事实并非如此,现实更像是这样:

  1. 你打电话console.log(x)
  2. 浏览器获取对 的引用x,并将“真实”调用排队console.log等待稍后。
  3. JavaScript 的各种其他位运行(或不运行)。
  4. 稍后,console.log来自(2)的调用将转储x到控制台的当前状态,但这x不一定与(2)x中的状态匹配。

在你的情况下,你正在这样做:

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)

这会让你看到两个不同的版本:新版本在(3)widgets中收到了一些东西,而原来的版本是空的。

当你这样做时:

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"事件。

于 2012-07-13T02:08:16.707 回答
1

请记住,[]在 JS 中只是 的别名new Array(),并且由于对象是通过引用传递的,因此 Session 模型的每个实例都将共享相同的数组对象。这会导致各种问题,包括看起来为空的数组。

为了使这项工作以您想要的方式工作,您需要在构造函数中初始化您的小部件数组。这将为每个 Session 对象创建一个唯一的小部件数组,并且应该可以缓解您的问题:

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);
    // ...
    }
});
于 2012-07-12T21:00:23.797 回答
0

在 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
] 
于 2012-07-12T19:43:40.030 回答