首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Backbone.Models this.get()是复制整个数组还是指向内存中的同一数组

Backbone.Models this.get()是复制整个数组还是指向内存中的同一数组
EN

Stack Overflow用户
提问于 2012-07-26 10:15:42
回答 1查看 6.6K关注 0票数 17
代码语言:javascript
复制
 Person = Backbone.Model.extend({
        defaults: {
            name: 'Fetus',
            age: 0,
            children: []
        },
        initialize: function(){
            alert("Welcome to this world");
        },
        adopt: function( newChildsName ){
            var children_array = this.get("children");
            children_array.push( newChildsName );
            this.set({ children: children_array });
        }
    });

    var person = new Person({ name: "Thomas", age: 67, children: ['Ryan']});
    person.adopt('John Resig');
    var children = person.get("children"); // ['Ryan', 'John Resig']

在这个示例代码中,我们有:

children_array =this.get(“孩子”)

我认为这只是指向内存中的相同数组(O(1)也是如此)。然而,我认为这将是一个设计底线,因为人们可以在不使用this.set()的情况下操作数组,这样事件侦听器就不会触发。

所以我猜它(以某种方式神奇地)复制了数组??

http://backbonejs.org/#Model-set

会发生什么?

编辑:我刚刚在https://github.com/documentcloud/backbone/blob/master/backbone.js的主干源代码中找到了实现(我已经在底部粘贴了相关代码)

获取退货:

代码语言:javascript
复制
return this.attributes[attr]

所以这只会指向内存中的同一数组,对吧?因此,可以在不使用set()的情况下更改数组,这是不好的。?我说的对吗?

代码语言:javascript
复制
get: function(attr) {
      return this.attributes[attr];
    },

    // Get the HTML-escaped value of an attribute.
    escape: function(attr) {
      var html;
      if (html = this._escapedAttributes[attr]) return html;
      var val = this.get(attr);
      return this._escapedAttributes[attr] = _.escape(val == null ? '' : '' + val);
    },

    // Returns `true` if the attribute contains a value that is not null
    // or undefined.
    has: function(attr) {
      return this.get(attr) != null;
    },

    // Set a hash of model attributes on the object, firing `"change"` unless
    // you choose to silence it.
    set: function(key, value, options) {
      var attrs, attr, val;

      // Handle both `"key", value` and `{key: value}` -style arguments.
      if (_.isObject(key) || key == null) {
        attrs = key;
        options = value;
      } else {
        attrs = {};
        attrs[key] = value;
      }

      // Extract attributes and options.
      options || (options = {});
      if (!attrs) return this;
      if (attrs instanceof Model) attrs = attrs.attributes;
      if (options.unset) for (attr in attrs) attrs[attr] = void 0;

      // Run validation.
      if (!this._validate(attrs, options)) return false;

      // Check for changes of `id`.
      if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];

      var changes = options.changes = {};
      var now = this.attributes;
      var escaped = this._escapedAttributes;
      var prev = this._previousAttributes || {};

      // For each `set` attribute...
      for (attr in attrs) {
        val = attrs[attr];

        // If the new and current value differ, record the change.
        if (!_.isEqual(now[attr], val) || (options.unset && _.has(now, attr))) {
          delete escaped[attr];
          (options.silent ? this._silent : changes)[attr] = true;
        }

        // Update or delete the current value.
        options.unset ? delete now[attr] : now[attr] = val;

        // If the new and previous value differ, record the change.  If not,
        // then remove changes for this attribute.
        if (!_.isEqual(prev[attr], val) || (_.has(now, attr) !== _.has(prev, attr))) {
          this.changed[attr] = val;
          if (!options.silent) this._pending[attr] = true;
        } else {
          delete this.changed[attr];
          delete this._pending[attr];
        }
      }

      // Fire the `"change"` events.
      if (!options.silent) this.change(options);
      return this;
    },
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2012-07-26 12:33:32

有文档记录的接口实际上并没有指定谁拥有数组引用,所以在这里只能靠自己了。如果您查看该实现,您将看到(正如您所做的那样),get只是直接从模型的内部attributes返回一个引用。这对于不可变类型(如数字、字符串和布尔值)工作得很好,但对于可变类型(如数组)则会遇到问题:您可以轻松地更改某些内容,而无需Backbone了解它。

主干模型似乎旨在包含原始类型。

调用set有三个原因

  1. 接口规范就是这么说的。
  2. 如果不调用set,就不会触发事件。
  3. 如果不调用set,就会绕过set的验证逻辑。

如果您正在处理数组和对象值,您只需小心。

请注意,getset的这种行为是一个实现细节,未来的版本可能会在如何处理非原语属性值方面变得更加智能。

数组属性(以及该问题的对象属性)的情况实际上比您最初怀疑的更糟糕。当您说m.set(p, v)时,Backbone不会认为该set是一个更改,如果是v === current_value_of_p,那么如果您取出一个数组:

代码语言:javascript
复制
var a = m.get(p);

然后修改它:

代码语言:javascript
复制
a.push(x);

并将其发回:

代码语言:javascript
复制
m.set(p, a);

您不会从模型中获得"change"事件,因为a === a;Backbone actually uses下划线的isEqual!==结合在一起,但效果在本例中是相同的。

例如,这个简单的小把戏:

代码语言:javascript
复制
var M = Backbone.Model.extend({});
var m = new M({ p: [ 1 ] });
m.on('change', function() { console.log('changed') });

console.log('Set to new array');
m.set('p', [2]);

console.log('Change without set');
m.get('p').push(3);

console.log('Get array, change, and re-set it');
var a = m.get('p'); a.push(4); m.set('p', a);

console.log('Get array, clone it, change it, set it');
a = _(m.get('p')).clone(); a.push(5); m.set('p', a);​

生成两个"change"事件:一个在第一个set之后,另一个在最后一个set之后。

演示:http://jsfiddle.net/ambiguous/QwZDv/

如果您查看set,您会注意到对于Backbone.Model属性有一些特殊的处理。

这里的基本教训很简单:

如果您打算使用可变类型作为属性值,则在退出时使用_.clone (或者,如果您需要更深的副本,则使用$.extend(true, ...) ),如果您有可能更改该值。

票数 32
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11661380

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档