Vac*_*tny 3 javascript arrays v8 node.js
I encountered strange behavior of Array.prototype.includes in one edge case.
Given that Array.prototype.includes works on bound context, one might use it like this (which is working)
expect(Array.prototype.includes.call([1, 2], 1))).toBe(true)
Run Code Online (Sandbox Code Playgroud)
simply put, we bound array [1, 2] and test 1 for inclusion.
Then consider, that many Array.prototype methods are able to bound context to provided callback, so for example Array.prototype.some can be combined with Object.prototype.hasOwnProperty like this
expect(["foo", "bar"].some(Object.prototype.hasOwnProperty, { foo: 0 })).toBe(true)
Run Code Online (Sandbox Code Playgroud)
Here, .some accepts two parameters, (callback, [thisArg]), where optional thisArg, when provided, is bound to callback, thus previous example binds { foo: 0 } to callback Object.prototype.hasOwnProperty and then tests all items in ["foo", "bar"] if at least one is own property of { foo: 0 }. This example is also working.
But something strange happen, if you try to use Array.prototype.includes as callback.
[0, 1].some(Array.prototype.includes, [1]) // => false
Run Code Online (Sandbox Code Playgroud)
here we bind array [1] to Array.prototype.includes and we test every item of [0, 1] if at least one is included. But this case returns false, which is against our expectation.
Strangely, if bound array contains other number than 1 or contains more than one item, the test passes
[0, 1].some(Array.prototype.includes, [0]) // => true
[0, 1].some(Array.prototype.includes, [1, 1]) // => true
// but
[0, 1].some(Array.prototype.includes, [1]) // => false
Run Code Online (Sandbox Code Playgroud)
It seems like array [1] is handled improperly.
Tested in Node v.11.11.0 Node v.8.11.3 and Chrome 73
I tested basically just V8 engine. Can anyone report output in Chakra?
T.J*_*der 14
It's not a bug in includes. :-)
The problem is that includes accepts an optional second parameter, which is the index at which to start searching, and some provides three arguments to its callback: The item, its index, and the object being searched.
So with
[0, 1].some(Array.prototype.includes, [1])
Run Code Online (Sandbox Code Playgroud)
These calls are made (effectively):
[1].includes(0, 0) - false, the array doesn't contain 0[1].includes(1, 1) - false, the array contains 1 at index 0, but the search starts at index 1This comes up periodically. For instance, when trying to use parseInt as a callback directly to convert an array of strings into an array of numbers, because of parseInt's second parameter (the number base, or radix, to use):
[0, 1].some(Array.prototype.includes, [1])
Run Code Online (Sandbox Code Playgroud)
The 9 and 7 fail becaue the calls are (effectively):
parseInt("6", 0) - works because parseInt ignores the invalid radix 0.parseInt("9", 1) - NaN because parseInt always returns NaN for radix 1parseInt("7", 2) - NaN because "7" is not a valid digit in base 2 (binary)The moral of the story: Remember the not-commonly-used arguments that map, some, forEach, and various other methods provide to callbacks. :-)
In one codebase I was working in, they had a clamp function that accepted a function and ensured that, regardless of how many arguments it was called with, it would only pass on the desired number of arguments. If you were using includes like this a lot, you could create a clamped includes:
console.log(["6", "9", "7"].map(parseInt));Run Code Online (Sandbox Code Playgroud)
The handy thing about that is that that includes is reusable.
Or of course, just use a wrapper function:
function clamped(fn, count) {
return function(...args) {
return fn.apply(this, args.slice(0, count));
}
}
const includes = clamped(Array.prototype.includes, 1);
console.log([0, 1].some(includes, [1])); // true
console.log([0, 1].some(includes, [3])); // falseRun Code Online (Sandbox Code Playgroud)
当然,这些全都是通用的解决方案。如果您特别想知道array是否a包含array 中的任何条目b,则可以构建更具体的实现以根据aand 的特性有效地处理该实现b。