17
 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のバックボーン ソース コードで実装を見つけました(関連するコードを下部に貼り付けました)。

返品を受け取る:

return this.attributes[attr]

つまり、これはメモリ内の同じ配列を指すだけですよね? したがって、set() を使用せずに配列を変更できますが、それは悪いことです.. ? 私は正しいですか?

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;
    },
4

1 に答える 1

32

文書化されたインターフェースは、実際には誰が配列参照を所有するかを指定していないので、ここであなたはあなた自身です。実装を見ると、(あなたがしたように)getモデルの内部から直接参照を返すだけであることがわかりますattributes。これは不変の型(数値、文字列、ブール値など)では問題なく機能しますが、配列などの不変の型では問題が発生します。Backboneがそれを知る方法がなくても、何かを簡単に変更できます。

バックボーンモデルは、プリミティブ型を含むことを目的としているようです。

電話をかける理由は3つありますset

  1. それがインターフェース仕様の指示です。
  2. を呼び出さない場合set、イベントはトリガーされません。
  3. を呼び出さない場合はset、を持っている検証ロジックをバイパスしますset

配列とオブジェクトの値を操作している場合は、注意する必要があります。

とのこの動作は実装の詳細であり、将来のバージョンでは、非プリミティブ属性値の処理方法についてよりスマートになる可能性があることに注意してgetくださいset


配列属性(およびそのことについてはオブジェクト属性)の状況は、実際には最初に予想したよりも悪いものです。あなたが言うとき、あなたが配列を引き出すならばm.set(p, v)、Backboneはそれsetが変更であるとは見なしません:v === current_value_of_p

var a = m.get(p);

次にそれを変更します:

a.push(x);

そしてそれを送り返します:

m.set(p, a);

"change"モデルからイベントを取得することはありませんa === a。バックボーンは実際にはアンダースコアisEqualとを組み合わせて使用​​し!==ますが、この場合の効果は同じです。

たとえば、この簡単なチカニーは次のとおりです。

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"2つのイベントを生成します。1つは最初のイベントの後、setもう1つは最後のイベントの後setです。

デモ: http: //jsfiddle.net/ambiguous/QwZDv/

を見ると、ssetである属性には特別な処理があることがわかりますBackbone.Model


ここでの基本的なレッスンは簡単です。

可変型を属性値として使用する場合、値を変更する可能性がある場合_.cloneは、途中で使用します(または$.extend(true, ...)、深いコピーが必要な場合は使用します)。

于 2012-07-26T04:33:31.573 に答える