documentcloud/backbone — pull request #1704 by tgriesser (2012-10-03 21:46:57+00:00)
Title: Overhauling set/change - adding major speed improvements, and `stealth` option.

This pull request is somewhat related to the one opened in #1688 - while that adds some performance increase and fixes some bugs, there is still at least [one edge case](https://github.com/tgriesser/backbone/blob/change-overhaul/test/model.js#L881) I know of where the expected result isn't what should be expected. This stems from the fact that it is difficult to manage the state by setting flags on various `_silent`, `_changed`, `_previousAttributes` objects and still know that is going on.

So I decided to take the same approach as the `Backbone.Events` and utilize an array, pushing each change as they occur to the last three spots `[key, value, unset]` - where `unset` is a boolean and determines whether this change is supposed to unset an attribute. These changes can build up in the array queue until the `change` event is called, at which point they are cycled through (last in first out) and each unique key is allowed to build up in an object once. If the current state of the attribute is (at this point) different than the current state, a change is queued & triggered for this attribute. Any unset keys are also appropriately taken care of and removed from the current model. The straight path through the `set` shows ~300% boost vs. the current, with the other two perfs looking to be about doubled - see below:

**Current: (after #1688 pull)**

Model: set Math.random()
Model: set rand() with an event
Model: set rand() with an attribute observer

59486
44786
48729

66866
48494
47541

66004
48441
46515

**New Version:**

Model: set Math.random()
Model: set rand() with an event
Model: set rand() with an attribute observer

186123
92970
83240

181988
96588
87229

179989
96134
97161

**(0.5.3)**

Model: set Math.random()
Model: set rand() with an event
Model: set rand() with an attribute observer

157495
103416
97974

155404
104338
96702

183208
113127
104073

This change also adds a new option `stealth` - allowing for the model to be completely reset to a clean state, with any stealth changes being both silent and clearing the current change array queue in the model.

The `stealth` feature isn't required, it could be taken out and other pieces would still work - however with all of the items now being handled in the queue, it could be helpful feature to set the model state not only silently, but with no trace - which is needed in at least once situation I know of - loading up the model with attributes when the Model is initialized.

```javascript
this.changed = {};
this._changes = {};
this._pending = {};
this.set(attrs, {silent: true});
// Reset change tracking.
this.changed = {};
this._changes = {};
this._pending = {};
```

This is a bit of a big internal change change, so it might require some thinking through even though all of the tests are passing - (especially with the addition of `stealth` - if this sounds like a good idea?), so any comments would be great. Thanks!