3.2 The pattern matching algorithm

3.2.1 Patterns

A pattern is either:

  • A variable: x
  • An object pattern: {«properties»}
  • An Array pattern: [«elements»]

The next three sections specify rules for handling these three cases in matching expressions.

3.2.2 Rules for variable

    1. x ← value (including undefined and null)
    1. x = value

3.2.3 Rules for object patterns

  • (2a) {«properties»} ← undefined (illegal value)

    1. throw new TypeError();
  • (2b) {«properties»} ← null (illegal value)

    1. throw new TypeError();
  • (2c) {key: «pattern», «properties»} ← obj

    1. «pattern» obj.key
    2. properties»} obj
  • (2d) {key: «pattern» = default_value, «properties»} ← obj

    1. const tmp = obj.key;
    2. if (tmp !== undefined) {
    3. «pattern» tmp
    4. } else {
    5. «pattern» default_value
    6. }
    7. properties»} obj
  • (2e) {} ← obj (no properties left)

    1. // We are finished

Rules 2a and 2b deal with illegal values. Rules 2c–2e loop over the properties of the pattern. In rule 2d, we can see that a default value provides an alternative to match against if there is no matching property in obj.

3.2.4 Rules for Array patterns

Array pattern and iterable. The algorithm for Array destructuring starts with an Array pattern and an iterable:

  • (3a) [«elements»] ← non_iterable (illegal value)
    if (!isIterable(non_iterable))

    1. throw new TypeError();
  • (3b) [«elements»] ← iterable
    if (isIterable(iterable))

    1. const iterator = iterable[Symbol.iterator]();
    2. «elements» iterator

Helper function:

  1. function isIterable(value) {
  2. return (value !== null
  3. && typeof value === 'object'
  4. && typeof value[Symbol.iterator] === 'function');
  5. }

Array elements and iterator. The algorithm continues with:

  • The elements of the pattern (left-hand side of the arrow)
  • The iterator that was obtained from the iterable (right-hand side of the arrow)

These are the rules:

  • (3c) «pattern», «elements» ← iterator

    1. «pattern» getNext(iterator) // undefined after last item
    2. «elements» iterator
  • (3d) «pattern» = default_value, «elements» ← iterator

    1. const tmp = getNext(iterator); // undefined after last item
    2. if (tmp !== undefined) {
    3. «pattern» tmp
    4. } else {
    5. «pattern» default_value
    6. }
    7. «elements» iterator
  • (3e) , «elements» ← iterator (hole, elision)

    1. getNext(iterator); // skip
    2. «elements» iterator
  • (3f) ...«pattern» ← iterator (always last part!)

    1. const tmp = [];
    2. for (const elem of iterator) {
    3. tmp.push(elem);
    4. }
    5. «pattern» tmp
  • (3g) ← iterator (no elements left)

    1. // We are finished

Helper function:

  1. function getNext(iterator) {
  2. const {done,value} = iterator.next();
  3. return (done ? undefined : value);
  4. }

An iterator being finished is similar to missing properties in objects.