Backbone:原型对象令人讨厌的行为

时间:2013-02-19 05:30:00

标签: javascript backbone.js prototype prototypal-inheritance

我知道这是javascript本身的问题(或行为),而不是Backbone的扩展方法,但我想知道什么是避免它的最佳策略。

让我们更好地把它放在代码中:

var MyModel = Backbone.Model.extend({
  value: 0,
  values: []
});

var myFirstModel = new MyModel();

myFirstModel.value // 0, as expected
myFirstModel.values // [], as expected

var mySecondModel = new MyModel();

mySecondModel.value = 2;
mySecondModel.values.push(2)

mySecondModel.value // 2, as expected
mySecondModel.values // [2], as expected

myFirstModel.value // 0, as expected
myFirstModel.values // [2], ... WAT!!!

我明白问题是我没有为mySecondModel.values分配一个新值我只是操作原型中的values变量,即MyModel.prototype.values(同样的)当然,任何其他对象的问题)

但是这很容易搞砸。最直观的是将它们视为INSTANCE变量,而不是每个实例共有的变量(基于类的语言中的静态或类变量)。

到目前为止,我发现的一般解决方案是初始化initialize方法中的每个变量,如下所示:

var MyModel = Backbone.Model.extend({
  initialize: function() {
    this.value = 0;
    this.values = [];
  }
});

这样一切都按预期工作,即使它不是一个简单值(如this.value)的必要条件,我发现在每种情况下都坚持这个原则要容易得多。

我想知道这个问题是否有更好(更优雅,更清晰)的解决方案

3 个答案:

答案 0 :(得分:2)

这是JavaScript的原型继承的效果,以及Array对象是引用类型的事实。您传递给extend的对象的键/值对将复制到MyModel的原型中,因此MyModel的所有实例都会共享它们。因为values是一个数组,所以在修改它时,可以为每个实例修改数组。

您在values内设置initialize所做的工作称为阴影原型,这是解决此问题的正确方法。

那就是说,在Backbone.Model的情况下,如果你试图处理模型的属性,你可以使用defaults函数提供这样的默认值:

var MyModel = Backbone.Model.extend({
  defaults: function() {
    return {
      value: 0,
      values: []
    }
  }
});

同样,这仅适用于实例的属性。

var inst = new MyModel();

// The defaults will be created for each new model,
// so this will always return a new array.
var values = inst.get('values');

对于您正在进行的操作,您在模型本身上指定属性的位置,由您自己设置initialize内的默认值,就像您所做的那样。

答案 1 :(得分:2)

您是否故意不将valuevalues设置为主干属性?如果在实例上设置属性,而不是将它们放在扩展主干模型定义中,它可能会按预期工作。

var MyModel = Backbone.Model.extend();

var myFirstModel = new MyModel({
  value: 0,
  values: []
});

console.log(myFirstModel.get('value');  // 0
console.log(myFirstModel.get('values'); // []

var mySecondModel = new MyModel({
  value: 2,
  values: [2]
});

//mySecondModel.value = 2;
//mySecondModel.values.push(2)

console.log(mySecondModel.get('value');  // 2
console.log(mySecondModel.get('values'); // [2]

console.log(myFirstModel.get('value');   // 0
console.log(myFirstModel.get('values');  // []

jsFiddle,检查控制台日志。

答案 2 :(得分:1)

我也曾经偶然发现这个问题并通过在模型中定义defaults方法解决了这个问题。

var MyModel = Backbone.Model.extend({
  defaults: function() {
    return {
      value: 0,
      values: []
    }
  }
});