Marius Schulz
Marius Schulz
Front End Engineer

ECMAScript 2016: Array.prototype.includes()

The Array.prototype.includes() method defined on the Array prototype is one of the two new features that ECMAScript 2016 standardizes. Array.prototype.includes() determines whether an array contains a given element and returns either true or false.

The other new feature is the exponentiation operator **, which provides a little syntactic sugar for Math.pow.

Egghead Lesson: Check If an Array Contains an Item Using Array.prototype.includes

#Array.prototype.includes() vs. Array.prototype.indexOf()

Up until now, you've probably compared the return value of the Array.prototype.indexOf() method against -1 to check whether an array contains a certain value:

const numbers = [4, 8, 15, 16, 23, 42];

if (numbers.indexOf(42) !== -1) {
  // ...
}

The Array.prototype.includes() method makes this check easier to read and more semantically meaningful to human readers:

const numbers = [4, 8, 15, 16, 23, 42];

if (numbers.includes(42)) {
  // ...
}

Notice how the if-condition almost reads like a regular English sentence. There's no more fiddling around with indices just to determine array membership.

#Looking for NaN

However, there's one edge case in which indexOf and includes behave differently, and that is NaN. Because the strict comparison NaN === NaN returns false, the indexOf method will return -1 when searching for NaN in an array:

assert([NaN].indexOf(NaN) === -1);

Most of the time, this is likely not what you intended. The includes method fixes this behavior and returns true:

assert([NaN].includes(NaN) === true);

As you would expect, the signed zero values +0 and -0 are treated the same as well:

assert([+0].includes(-0) === true);
assert([-0].includes(+0) === true);

#Providing a Start Index

The indexOf method accepts an optional second parameter named fromIndex that specifies at which index in the array to start the search:

assert([100, 200, 300].indexOf(100, 0) === 0);
assert([100, 200, 300].indexOf(100, 1) === -1);

For consistency, the includes method accepts this parameter as well:

assert([100, 200, 300].includes(100, 0) === true);
assert([100, 200, 300].includes(100, 1) === false);

#A Polyfill for Array.prototype.includes()

There's also a spec-compliant polyfill available on MDN that allows you to use Array.prototype.includes() today without worrying about browser compatibility:

// https://tc39.github.io/ecma262/#sec-array.prototype.includes
if (!Array.prototype.includes) {
  Object.defineProperty(Array.prototype, 'includes', {
    value: function(searchElement, fromIndex) {

      if (this == null) {
        throw new TypeError('"this" is null or not defined');
      }

      // 1. Let O be ? ToObject(this value).
      var o = Object(this);

      // 2. Let len be ? ToLength(? Get(O, "length")).
      var len = o.length >>> 0;

      // 3. If len is 0, return false.
      if (len === 0) {
        return false;
      }

      // 4. Let n be ? ToInteger(fromIndex).
      //    (If fromIndex is undefined, this step produces the value 0.)
      var n = fromIndex | 0;

      // 5. If n ≥ 0, then
      //  a. Let k be n.
      // 6. Else n < 0,
      //  a. Let k be len + n.
      //  b. If k < 0, let k be 0.
      var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);

      function sameValueZero(x, y) {
        return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y));
      }

      // 7. Repeat, while k < len
      while (k < len) {
        // a. Let elementK be the result of ? Get(O, ! ToString(k)).
        // b. If SameValueZero(searchElement, elementK) is true, return true.
        if (sameValueZero(o[k], searchElement)) {
          return true;
        }
        // c. Increase k by 1.
        k++;
      }

      // 8. Return false
      return false;
    }
  });
}

#Further Reading

For more details, check out the original feature proposal by Domenic Denicola and Rick Waldron, the current spec draft, or the documentation on MDN.