Array.prototype.includes 中的错误?
Bug in Array.prototype.includes?
我在一种极端情况下遇到了 Array.prototype.includes
的奇怪行为。
鉴于 Array.prototype.includes
适用于绑定上下文,人们可能会像这样使用它(这是有效的)
expect(Array.prototype.includes.call([1, 2], 1))).toBe(true)
简单地说,我们绑定数组 [1, 2]
并测试 1
是否包含。
然后考虑,许多 Array.prototype 方法能够将上下文绑定到提供的回调,因此例如 Array.prototype.some
可以像这样与 Object.prototype.hasOwnProperty
组合
expect(["foo", "bar"].some(Object.prototype.hasOwnProperty, { foo: 0 })).toBe(true)
这里,.some
接受两个参数,(callback, [thisArg])
,其中可选的 thisArg
,当提供时,绑定到回调,因此前面的示例将 { foo: 0 }
绑定到回调 Object.prototype.hasOwnProperty
然后测试 ["foo", "bar"]
中的所有项目,如果至少有一个是自己的 属性 of { foo: 0 }
。这个例子也有效。
但是如果您尝试使用 Array.prototype.includes
作为回调,就会发生一些奇怪的事情。
[0, 1].some(Array.prototype.includes, [1]) // => false
这里我们将数组 [1]
绑定到 Array.prototype.includes
并且我们测试 [0, 1]
的每一项是否至少包含一个。但是这个案例returns false,出乎我们的意料。
奇怪的是,如果绑定数组包含 1
以外的其他数字或包含多个项目,则测试通过
[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
似乎数组 [1]
处理不当。
测试于
节点 v.11.11.0
节点 v.8.11.3
和 Chrome 73
我基本上只测试了V8引擎。任何人都可以报告 Chakra 的输出吗?
这不是 includes
中的错误。 :-)
问题是 includes
接受一个可选的第二个参数,它是开始搜索的索引,并且 some
为其回调提供了三个参数:项目、它的索引和正在搜索的对象。
所以
[0, 1].some(Array.prototype.includes, [1])
进行了这些调用(有效):
[1].includes(0, 0)
- 错误,数组不包含 0
[1].includes(1, 1)
- 错误,数组在索引 0
处包含 1
,但搜索从索引 1
开始
这会定期出现。例如,当尝试使用 parseInt
作为回调直接将字符串数组转换为数字数组时,因为 parseInt
的第二个参数(要使用的数字基数或基数) :
console.log(["6", "9", "7"].map(parseInt));
9
和 7
失败是因为调用(有效):
parseInt("6", 0)
- 有效是因为 parseInt
忽略了无效基数 0
.
parseInt("9", 1)
- NaN
因为 parseInt
对于基数 1
总是 returns NaN
parseInt("7", 2)
- NaN
因为 "7"
不是基数 2(二进制)中的有效数字
故事的寓意:记住map
、some
、forEach
和各种不常用的论点其他方法提供给回调。 :-)
在我工作的一个代码库中,他们有一个 clamp
函数,它接受一个函数并确保无论调用它有多少参数,它只会传递所需数量的参数.如果你经常这样使用 includes
,你可以创建一个固定的 includes
:
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])); // false
方便的是 includes
是可重复使用的。
当然,也可以使用包装函数:
console.log([0, 1].some(function(entry) {
return this.includes(entry);
}, [1])); // true
console.log([0, 1].some(function(entry) {
return this.includes(entry);
}, [3])); // false
当然,这些都是一般解决方案。如果您特别想知道数组 a
是否包含数组 b
中的任何条目,您可以构建更具体的实现来有效地处理该问题,具体取决于 a
和 b
.
我在一种极端情况下遇到了 Array.prototype.includes
的奇怪行为。
鉴于 Array.prototype.includes
适用于绑定上下文,人们可能会像这样使用它(这是有效的)
expect(Array.prototype.includes.call([1, 2], 1))).toBe(true)
简单地说,我们绑定数组 [1, 2]
并测试 1
是否包含。
然后考虑,许多 Array.prototype 方法能够将上下文绑定到提供的回调,因此例如 Array.prototype.some
可以像这样与 Object.prototype.hasOwnProperty
组合
expect(["foo", "bar"].some(Object.prototype.hasOwnProperty, { foo: 0 })).toBe(true)
这里,.some
接受两个参数,(callback, [thisArg])
,其中可选的 thisArg
,当提供时,绑定到回调,因此前面的示例将 { foo: 0 }
绑定到回调 Object.prototype.hasOwnProperty
然后测试 ["foo", "bar"]
中的所有项目,如果至少有一个是自己的 属性 of { foo: 0 }
。这个例子也有效。
但是如果您尝试使用 Array.prototype.includes
作为回调,就会发生一些奇怪的事情。
[0, 1].some(Array.prototype.includes, [1]) // => false
这里我们将数组 [1]
绑定到 Array.prototype.includes
并且我们测试 [0, 1]
的每一项是否至少包含一个。但是这个案例returns false,出乎我们的意料。
奇怪的是,如果绑定数组包含 1
以外的其他数字或包含多个项目,则测试通过
[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
似乎数组 [1]
处理不当。
测试于 节点 v.11.11.0 节点 v.8.11.3 和 Chrome 73
我基本上只测试了V8引擎。任何人都可以报告 Chakra 的输出吗?
这不是 includes
中的错误。 :-)
问题是 includes
接受一个可选的第二个参数,它是开始搜索的索引,并且 some
为其回调提供了三个参数:项目、它的索引和正在搜索的对象。
所以
[0, 1].some(Array.prototype.includes, [1])
进行了这些调用(有效):
[1].includes(0, 0)
- 错误,数组不包含0
[1].includes(1, 1)
- 错误,数组在索引0
处包含1
,但搜索从索引1
开始
这会定期出现。例如,当尝试使用 parseInt
作为回调直接将字符串数组转换为数字数组时,因为 parseInt
的第二个参数(要使用的数字基数或基数) :
console.log(["6", "9", "7"].map(parseInt));
9
和 7
失败是因为调用(有效):
parseInt("6", 0)
- 有效是因为parseInt
忽略了无效基数0
.parseInt("9", 1)
-NaN
因为parseInt
对于基数1
总是 returns parseInt("7", 2)
-NaN
因为"7"
不是基数 2(二进制)中的有效数字
NaN
故事的寓意:记住map
、some
、forEach
和各种不常用的论点其他方法提供给回调。 :-)
在我工作的一个代码库中,他们有一个 clamp
函数,它接受一个函数并确保无论调用它有多少参数,它只会传递所需数量的参数.如果你经常这样使用 includes
,你可以创建一个固定的 includes
:
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])); // false
方便的是 includes
是可重复使用的。
当然,也可以使用包装函数:
console.log([0, 1].some(function(entry) {
return this.includes(entry);
}, [1])); // true
console.log([0, 1].some(function(entry) {
return this.includes(entry);
}, [3])); // false
当然,这些都是一般解决方案。如果您特别想知道数组 a
是否包含数组 b
中的任何条目,您可以构建更具体的实现来有效地处理该问题,具体取决于 a
和 b
.