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: 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;

    type: 'embiggened',
    amount: amount

Usage: Mutation


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 = 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





Array (splices)


Array (splices)


Path (leaf mutation)


Path (leaf mutation)


Polymer Usage

  • Lots of work has gone into making dirty-checking a workable bridge strategy
  • Seemingly simple Polymer apps have already demonstrated > 10k bindings


By Rafael Weinstein