4个错误使用JavaScript数组方法的案例

译者按: 做一个有追求的工程师,代码不是随便写的!

本文采用意译,版权归原作者所有

小编推荐:Fundebug专注于 JavaScript、微信小程序、微信小游戏,Node.js 和 Java 线上 bug 实时监控。真的是一个很好用的 bug 监控服务,众多大佬公司都在使用。

我保证这是一篇可以快速阅读并吸收的文章。在过去的几个月里,我检查的所有的 pull request 中,都犯了这 4 个的错误。我写这篇文章的另一个原因在于我自己也犯过这些错。我们来看看如何正确地使用它们!

1. 不要使用 Array.indexOf,使用 Array.includes

“如果你要在数组中查找元素,使用 Array.indexOf!”。记得在我学习 JavaScript 课程时候,有这样一句话。这句话没错,确实可以这么使用!

根据 MDN 文档:“Array.indexOf 会返回被查找元素第一个匹配的位置的下标。”因此,如果后面需要用到这个索引,Array.indexOf 是一个很好的解法。

但是,我们要解决的问题是:查找数组中是否包含某个元素。这是一个 Yes/No 的问题,是一个返回布尔类型的真假问题。因此,我建议使用 Array.includes,它会返回一个布尔值。

"use strict";

const characters = [
"ironman",
"black_widow",
"hulk",
"captain_america",
"hulk",
"thor"
];

console.log(characters.indexOf("hulk"));
// 2
console.log(characters.indexOf("batman"));
// -1

console.log(characters.includes("hulk"));
// true
console.log(characters.includes("batman"));
// false

2. 不要使用 Array.filter,使用 Array.find

Array.filter 是一个很有用的函数,它返回一个满足过滤条件的新数组。正如其名字表达的含义,它是用来做过滤的。

但是,如果我们知道我们要的结果只有一个元素的时候,我就不建议使用它了。比如,如果我们的回调函数定义用一个唯一的 ID 来过滤,那么结果必然唯一了。在这个情况下,Array.filter 会返回只有一个元素的数组。因为既然能通过一个特定的 ID 来查找,我们已经确定只有一个元素了,那么使用数组就没有意义。

另外,我们再来聊聊性能问题。为了返回所有匹配的元素,Array.filter 需要查找整个数组。可以想象一下,如果有上百个元素满足过滤条件,那么返回的数组就很大。

为了避免这样的情况,我建议使用 Array.find。它仅仅返回第一个满足过滤条件的元素。而且,Array.find 会在查找到第一个满足条件的元素后就结束执行,而不会查找整个数组。

"use strict";

const characters = [
{ id: 1, name: "ironman" },
{ id: 2, name: "black_widow" },
{ id: 3, name: "captain_america" },
{ id: 4, name: "captain_america" }
];

function getCharacter(name) {
return character => character.name === name;
}

console.log(characters.filter(getCharacter("captain_america")));
// [
// { id: 3, name: 'captain_america' },
// { id: 4, name: 'captain_america' },
// ]

console.log(characters.find(getCharacter("captain_america")));
// { id: 3, name: 'captain_america' }

3. 不要使用 Array.find,使用 Array.some

我承认我犯过很多次错误。后来,一个很要好的朋友让我去看看MDN 的文档,说有更好的解决方案。这个情况和刚刚提到的 Array.indexOf/Array.includes 很像。

在前面的例子中,我们看到 Array.find 接受一个过滤函数,返回满足的元素。那么,如果我们要查找一个数组是否包含某个元素的时候,Array.find 是否是最佳的方案呢?可能不是,因为它返回的是元素具体的值,而不是布尔值。

我推荐大家使用 Array.some,它会返回布尔值。

"use strict";

const characters = [
{ id: 1, name: "ironman", env: "marvel" },
{ id: 2, name: "black_widow", env: "marvel" },
{ id: 3, name: "wonder_woman", env: "dc_comics" }
];

function hasCharacterFrom(env) {
return character => character.env === env;
}

console.log(characters.find(hasCharacterFrom("marvel")));
// { id: 1, name: 'ironman', env: 'marvel' }

console.log(characters.some(hasCharacterFrom("marvel")));
// true

4. 不要使用 Array.map 和 Array.filter 组合,使用 Array.reduce

Array.reduce 有点难以理解!但是,如果我们每次在同时使用 Array.filter 和 Array.map 的时候,你是否觉察到需要点东西,对不?

我的意思是:我们对整个数组循环了 2 遍。第一次是过滤返回一个新的数组,第二次通过 map 又构造一个新的数组。我们使用了两个数组方法,每一个方法都有各自的回调函数,而且 Array.filter 返回的数组以后再也不会用到。

为了避免低效率,我建议使用 Array.reduce。同样的结果,更优雅的代码!请看下面的例子:

"use strict";

const characters = [
{ name: "ironman", env: "marvel" },
{ name: "black_widow", env: "marvel" },
{ name: "wonder_woman", env: "dc_comics" }
];

console.log(
characters
.filter(character => character.env === "marvel")
.map(character =>
Object.assign({}, character, { alsoSeenIn: ["Avengers"] })
)
);
// [
// { name: 'ironman', env: 'marvel', alsoSeenIn: ['Avengers'] },
// { name: 'black_widow', env: 'marvel', alsoSeenIn: ['Avengers'] }
// ]

console.log(
characters.reduce((acc, character) => {
return character.env === "marvel"
? acc.concat(
Object.assign({}, character, { alsoSeenIn: ["Avengers"] })
)
: acc;
}, [])
);
// [
// { name: 'ironman', env: 'marvel', alsoSeenIn: ['Avengers'] },
// { name: 'black_widow', env: 'marvel', alsoSeenIn: ['Avengers'] }
// ]

备注

malgosiastp and David Piepgrass在评论中提到:在使用 Array.find 和 Array.includes 前请注意检查兼容性,因为最新的 IE 不支持。

关于Fundebug

Fundebug专注于JavaScript、微信小程序、支付宝小程序线上应用实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了80亿+错误事件。欢迎大家免费试用

版权声明

转载时请注明作者 Fundebug以及本文地址:
https://blog.fundebug.com/2018/09/06/make-better-use-of-arrays/

您的用户遇到BUG了吗?

体验Demo 免费使用