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
.
#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.