does Backbone.Models this.get() copy an entire array or point to the same array in memory

后端 未结 1 1040
无人共我
无人共我 2020-12-03 03:50
 Person = Backbone.Model.extend({
        defaults: {
            name: \'Fetus\',
            age: 0,
            children: []
        },
        initialize: functi         


        
相关标签:
1条回答
  • 2020-12-03 04:15

    The documented interface doesn't actually specify who owns the array reference so you're on your own here. If you look at the implementation, you'll see (as you did) that get just returns a reference straight out of the model's internal attributes. This works fine with immutable types (such as numbers, strings, and booleans) but runs into problems with mutable types such as arrays: you can easily change something without Backbone having any way of knowing about it.

    Backbone models appear to be intended to contain primitive types.

    There are three reasons to call set:

    1. That's what the interface specification says to do.
    2. If you don't call set, you don't trigger events.
    3. If you don't call set, you'll bypass the validation logic that set has.

    You just have to be careful if you're working with array and object values.

    Note that this behavior of get and set is an implementation detail and future versions might get smarter about how they handle non-primitive attribute values.


    The situation with array attributes (and object attributes for that matter) is actually worse than you might initially suspect. When you say m.set(p, v), Backbone won't consider that set to be a change if v === current_value_of_p so if you pull out an array:

    var a = m.get(p);
    

    then modify it:

    a.push(x);
    

    and send it back in:

    m.set(p, a);
    

    you won't get a "change" event from the model because a === a; Backbone actually uses Underscore's isEqual combined with !== but the effect is the same in this case.

    For example, this simple bit of chicanery:

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

    produces two "change" events: one after the first set and one after the last set.

    Demo: http://jsfiddle.net/ambiguous/QwZDv/

    If you look at set you'll notice that there is some special handling for attributes that are Backbone.Models.


    The basic lesson here is simple:

    If you're going to use mutable types as attribute values, _.clone them on the way out (or use $.extend(true, ...) if you need a deep copy) if there is any chance that you'll change the value.

    0 讨论(0)
提交回复
热议问题