简体   繁体   English

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

[英]Backbone: annoying behaviour of prototype object

I understand this is a problem (or behaviour) of javascript itself rather than Backbone's extend method, but I'd like to know what is the best strategy to avoid it. 我知道这是javascript本身的问题(或行为),而不是Backbone的扩展方法,但我想知道什么是避免它的最佳策略。

Let's better put it in code: 我们最好把它放在代码中:

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!!!

I do understand that the problem is I'm not assigning a new value to mySecondModel.values I'm just operating on the values variable that is in the prototype, that is MyModel.prototype.values (same problem with any other object, of course) 我明白问题是我没有为mySecondModel.values赋值。我只是在原型中的values变量上操作,即MyModel.prototype.values(与任何其他对象相同的问题,课程)

But it's very easy to mess with that. 但是这很容易搞砸。 The most intuitive thing is to just think of those as INSTANCE variables, and not variables common to every instance (static or class variables in class based languages). 最直观的是将它们视为INSTANCE变量,而不是每个实例共有的变量(基于类的语言中的静态或类变量)。

So far now the general solution I've found is to initialize every variable in the initialize method, like this: 到目前为止,我发现的一般解决方案是初始化initialize方法中的每个变量,如下所示:

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

That way everything works as expected, and even though it wouldn't be neccesary for a simple value (like this.value) I find it much easier to just stick to this prnciple in every case. 这样一切都按预期工作,即使它不是一个简单值(如this.value)的必要条件,我发现在每种情况下坚持这个原则要容易得多。

I'm wondering if there's some better (more elegant, clearer) solution to this problem 我想知道是否有一些更好(更优雅,更清晰)的解决方案来解决这个问题

This is an effect of JavaScript's prototypical inheritance and the fact that Array objects are reference types. 这是JavaScript的原型继承的效果,以及Array对象是引用类型的事实。 The key/value pairs of the object you pass to extend are copied onto the prototype of MyModel , so they will be shared by all instances of MyModel . 将传递给对象的键/值对extend被复制到的原型MyModel ,因此它们将被所有实例共享MyModel Because values is an array, when you modify it, you modify the array for every instance. 由于values是一个数组,因此在修改它时,可以为每个实例修改数组。

What you are doing by setting values inside initialize is called shadowing the prototype, and it is the correct way to solve this issue. 通过在initialize设置values您正在做的事情称为遮蔽原型,这是解决此问题的正确方法。

That said, in the case of Backbone.Model , if you are attempting to deal with the model's attributes, you can use the defaults function to provide defaults like this: 也就是说,在Backbone.Model的情况下,如果您尝试处理模型的属性,可以使用defaults函数来提供如下默认值:

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

Again, this is only for attributes of an instance. 同样,这仅适用于实例的属性。

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

For what you are doing, where you are specifying properties on the model itself, it is up to you to set the defaults inside of initialize , as you have done. 对于您正在进行的操作,您在模型本身上指定属性的位置,由您自己设置initialize内部的默认值,就像您所做的那样。

Are you intentionally not setting value and values as backbone attributes? 您是否故意不将valuevalues设置为主干属性? If you set attributes on an instance, instead of putting them in the extended backbone model definition, it might work how you expect. 如果在实例上设置属性,而不是将它们放在扩展主干模型定义中,它可能会按您的预期工作。

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 , check the console log. jsFiddle ,检查控制台日志。

I too had stumbled across this problem some time back and solved it by defining a defaults method in the model. 我也曾经偶然发现这个问题并通过在模型中定义默认方法来解决它。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM