documentcloud/backbone — pull request #1712 by tgriesser (2012-10-05 12:54:58+00:00)
Title: Refactoring set/change for speed improvements & bug fix.

This pull request follows the change from #1688 - while that shows a performance increase and fixes some bugs, changing an item silently inside of the final `change` event, and then changing back to the original value later [triggers a change] (https://github.com/tgriesser/backbone/blob/new-set-change/test/model.js#L881), which isn't the expected functionality. The current model change approach manages the "changing" state of the model by setting flags on various objects - `_changes `, `changed`, `pending`, and `_previousAttributes`, and it is difficult to keep all of these in sync.

So I decided to take the same approach as the `Backbone.Events` and utilize an array, `this._modelState`, pushing each change as they occur to the last three spots `[key, value, unset]` - where `unset` is a boolean to determine whether the attribute should be unset. 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 added to the local change object once.

At this point, if the attribute is different than the current state of the model or is being unset, a change is queued & triggered for this attribute. Any unset keys are also appropriately taken care of and removed from the current model. Running the Backbone "speed suite" shows major improvements (> 200%) vs the current "master" - and in all cases nearing the speed of the simpler `change` system of 0.5.3:

A new speed test is also added for cases where multiple items are silently set on the model, before triggering, showing major improvements vs. the current:

```javascript
var silentModel = new Backbone.Model;
silentModel.on('change', fn);

JSLitmus.test('Model: silent sets with rand(), with an attr observer', function () {
for (var i=0;i<10;i++) {
silentModel.set({number:Math.random()}, {silent:true});
}
silentModel.set({number:'one'});
});
```

**Results:**

New Change/Set
Current Master
(0.5.3)

Model: set Math.random()
234,952
99,054
266,493

Model: set rand() with an event
124,090
68,551
154,304

Model: set rand() with an attribute observer
113,123
65,692
143,242

Model: silent sets with rand(), with an attribute observer
143,930
27,389
n/a