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']
In this example code we have:
children_array = this.get("children")
I was thinking this would just point to the same array in memory (and so would be O(1)). However then I thought that would be a design floor because one could manipulate the array without using this.set() and then event listeners wouldn't fire.
So I'm guessing it (somehow magically) copies the array??
http://backbonejs.org/#Model-set
What happens?
edit: I just found the implementation in the backbone source code at https://github.com/documentcloud/backbone/blob/master/backbone.js (I've pasted relevant code at bottom)
Get returns:
return this.attributes[attr]
so this would just point to the same array in memory right? So one could change the array without using set() and that would be bad.. ? am i correct?
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;
},
Backbone. js gives structure to web applications by providing models with key-value binding and custom events, collections with a rich API of enumerable functions, views with declarative event handling, and connects it all to your existing API over a RESTful JSON interface.
The Backbone. js collection models specify the array or models which are created inside of a collection. Syntax: collection.
10) What is the usage of Backbone. sync? When Backbone. js wants to save or read a model to the Server, it calls out a function called Backbone.
Backbone. js is a model view controller (MVC) Web application framework that provides structure to JavaScript-heavy applications. This is done by supplying models with custom events and key-value binding, views using declarative event handling and collections with a rich application programming interface (API).
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
:
set
, you don't trigger events.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.Model
s.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With