Object.observe
Design update & implementation report
Rafael Weinstein, Sept 2013
Observing Arrays
Linear behavior?
- E.g. splice, shift, unshift
- Expose linear operations which "feel" constant in modern VMs
- Want to avoid broadcasting lots of change records when one or two will suffice
Basic usage
Object.observe(obj, callback)
-
Observes an object for "intrinsic" changes:
- 'new'
- 'updated'
- 'deleted'
- 'prototype'
- 'reconfigured'
Synthetic Changes
Object.getNotifier(object).notify({
object: obj,
type: "synthetic"
});
-
Allows
- Accessors to notify when private state has changed
- Domain (user-land) objects to broadcast higher-level semantic changes
Evolving implementation?
- If objects start broadcasting new change types, will existing code break?
Solution: performChange
- Design the pattern for objects to describe complex changes more compactly
-
Existing code should never be surprised with unknown change types
- Only observers who can understand the more compact description should receive it
- More 'simple-minded' observers can always understand changes in terms of (underlying) intrinsic property changes
Usage: Observers
var square = new Square(0, 0, 10, 10); function simpleton() { // do simpleton stuff } Object.observe(square, simpleton); function smartypants() { // do smart stuff } Object.observe(square, smartypants,
[‘new’, ‘updated’, ‘deleted’, 'embiggened'] );
Usage: Object
embiggin: function(int amount) {
notifier.performChange('embiggened', () => {
this.width *= amount;
this.height *= amount;
});
notifier.notify({
type: 'embiggened',
amount: amount
});
}
Usage: Mutation
square.embiggen(2);
simpleton receives:
{
object: square, type: 'updated',
name: 'width', oldValue: 10
},
{
object: square, type: 'updated',
name: 'height', oldValue: 10
}
smartypants receives:
{
object: square, type: 'embiggened', amount: 2
}
Array mutation methods
-
Specified to use performChange('splice', ... )
- Push/Pop/Shift/Unshift/Splice
- Property index updates which extend length
- length updates which extend or truncate length
- reverse()? sort()?
-
‘splice’ changeRecord type:
{
object: arr,
type: ‘splice’,
index: <number>,
removed: <array>,
addedCount: <number>
}
Array.observe
Array.observe = function(obj, callback) { return Object.observe(obj, callback, ['new', 'updated', 'deleted', 'splice']);
}
- Purpose is observing an Array's "vector-ness".
- If you care about an Array's "object-ness", use Object.observe.
Thought Experimental:
Map, Insert K=>V
{ object: map, type: 'set', key: k }
Map, Replace V for K
{ object: map, type: 'replace', key: k, oldValue: v }
Map, Delete K
{ object: map, type: 'deleted', key: k, oldValue: v }
Set, Insert K
{ object: set, type: 'set', key: k }
Set, Delete K
{ object: set, type: 'deleted', key: k }
Initial V8 perf
Dirty-checking vs Object.observe
Object
Dirty-checking
Object
Object.observe
Array (splices)
Dirty-checking
Array (splices)
Object.observe
Path (leaf mutation)
Dirty-check
Path (leaf mutation)
Object.observe
Polymer Usage
- Lots of work has gone into making dirty-checking a workable bridge strategy
-
Seemingly simple Polymer apps have already demonstrated > 10k bindings
Object.observe-sept2013
By Rafael Weinstein
Object.observe-sept2013
- 9,139