Permalink
Find file
b0f3a5c Jun 25, 2016
@sebmarkbage @pitaj
128 lines (110 sloc) 2.92 KB

Object Spread Initializer

Specification

Examples

Shallow Clone (excluding prototype)

let aClone = { ...a };

Desugars into:

let aClone = Object.assign({}, a);

Merging Two Objects

let ab = { ...a, ...b };

Desugars into:

let ab = Object.assign({}, a, b);

Overriding Properties

let aWithOverrides = { ...a, x: 1, y: 2 };
// equivalent to
let aWithOverrides = { ...a, ...{ x: 1, y: 2 } };
// equivalent to
let x = 1, y = 2, aWithOverrides = { ...a, x, y };

Desugars into:

let aWithOverrides = Object.assign({}, a, { x: 1, y: 2 });

Default Properties

let aWithDefaults = { x: 1, y: 2, ...a };

Desugars into:

let aWithDefaults = Object.assign({}, { x: 1, y: 2 }, a);
// which can be optimized without the empty object
let aWithDefaults = Object.assign({ x: 1, y: 2 }, a);

Multiple Merges

// getters on a are executed twice
let xyWithAandB = { x: 1, ...a, y: 2, ...b, ...a };

Desugars into:

let xyWithAandB = Object.assign({ x: 1 }, a, { y: 2 }, b, a);

Getters on the Object Initializer

// Does not throw because .x isn't evaluated yet. It's defined.
let aWithXGetter = { ...a, get x() { throw new Error('not thrown yet') } };

Desugars into:

let aWithXGetter = {};
Object.assign(aWithXGetter, a);
Object.defineProperty(aWithXGetter, "x", {
  get(){ throw new Error('not thrown yet') },
  enumerable : true,
  configurable : true
});

Getters in the Spread Object

// Throws because the .x property of the inner object is evaluated when the
// property value is copied over to the surrounding object initializer.
let runtimeError = { ...a, ...{ get x() { throw new Error('thrown now') } } };

Setters Are Not Executed When They're Redefined

let z = { set x() { throw new Error(); }, ...{ x: 1 } }; // No error

Null/Undefined Are Ignored

let emptyObject = { ...null, ...undefined }; // no runtime error

Updating Deep Immutable Object

let newVersion = {
  ...previousVersion,
  name: 'New Name', // Override the name property
  address: { ...previousVersion.address, zipCode: '99999' } // Update nested zip code
  items: [...previousVersion.items, { title: 'New Item' }] // Add an item to the list of items
};

Prior Art

Successor-ML

{ ... = a, x = 1, y = 2 }

http://successor-ml.org/index.php?title=Functional_record_extension_and_row_capture

Elm

{ a | x <- 1, y <- 2 }

http://elm-lang.org/learn/Records.elm#updating-records

OCaml

{ a with x = 1; y = 2 }

https://realworldocaml.org/v1/en/html/records.html#functional-updates

Haskell

a { x = 1, y = 2 }

http://www.haskell.org/haskellwiki/Default_values_in_records