JS Bits with Bill
JS Bits with Bill

JS Bits with Bill

Symbols Are Your Friend Series Part VII: Symbol.asyncIterator, Symbol.hasInstance & Symbol.isConcatSpreadable

Symbols Are Your Friend Series Part VII: Symbol.asyncIterator, Symbol.hasInstance & Symbol.isConcatSpreadable

JS Bits with Bill's photo
JS Bits with Bill

Published on Nov 20, 2020

2 min read

Symbols Are Your Friend Series


This is it - the last of the well-known symbols! Today we'll look at:

  • Symbol.asyncIterator
  • Symbol.hasInstance
  • Symbol.isConcatSpreadable

Symbol.asyncIterator Similar to Symbol.iterator, Symbol.asyncIterator functions the same way, except they return promises. This enables us to make an object that has asynchronous functionality iterable:

// Create iterable object
const todos = {
  [Symbol.asyncIterator]: function() {
    let i = 1;
    const limit = 4;

    return {
      async next() {
        if (i === limit) return { done: true };

        i++;
        return { value: await fetchTodo(i), done: false }
      }
    }
  },
};

// Fetch a todo object
async function fetchTodo(num) {
  const url = `https://jsonplaceholder.typicode.com/todos/${num}`;
  const response = await fetch(url);
  return await response.json();
}

// Asynchronously iterate through todos
(async () => {
  for await (const todo of todos) {
    console.log(todo.title);
  }
})();

With this example, we're effective making our todos object async iterable. Symbol.asyncIterator is also commonly implemented with generator functions but we'll explore those in another article.


Symbol.hasInstance This symbol is used to determine whether or not a constructor has an instance of another object when instanceof is used.

This allows us to customize the behavior of instanceof when it operates on our constructor object:

class PowerRanger {
  constructor(name) {
    this.name = name;
  }

  static [Symbol.hasInstance](instance) {
    const rangersRegEx = /jason|zack|kim|billy|trini/i;
    return rangersRegEx.test(instance.name);
  }
}

const jason = new PowerRanger('Jason');
const bob = new PowerRanger('Bob');

jason instanceof PowerRanger; // true
bob instanceof PowerRanger; // false

Symbol.isConcatSpreadable This symbol is a toggle that indicates if an object should be flatted when Array.prototype.concat() is used:

const pokemon = ['Eevee', 'Snorlax', 'Ditto'];
const transformers = ['Jazz', 'Grimlock', 'Arcee'];

pokemon.concat(transformers);
// Returns ["Eevee", "Snorlax", "Ditto", "Jazz", "Grimlock", "Arcee"]
const pokemon = ['Eevee', 'Snorlax', 'Ditto'];
const transformers = ['Jazz', 'Grimlock', 'Arcee'];

transformers[Symbol.isConcatSpreadable] = false;

pokemon.concat(transformers);
// Returns ["Eevee", "Snorlax", "Ditto", Array(3)]

A great use for this Symbol is to force normally unflattenable array-like objects to flatten:

const pokemon = ['Eevee', 'Snorlax', 'Ditto'];
const transformers = {
  [Symbol.isConcatSpreadable]: true, 
  length: 3, // Length needed to specify # of props to add
  0: 'Jazz', 
  1: 'Grimlock',
  2: 'Arcee'
};

pokemon.concat(transformers);
// Returns ["Eevee", "Snorlax", "Ditto", "Jazz", "Grimlock", "Arcee"]

This concludes the Symbols Are Your Friend series! We've just gone from Symbol zero to hero! 🦸


Check out more #JSBits at my blog, jsbits-yo.com. Or follow me on Twitter.

 
Share this