如何在 JavaScript 中更好地利用数组


Posted in Javascript onSeptember 27, 2018

本文短小精悍,我保证。在过去的数个月里,我注意到在我审阅的 pull request 中有四个(关于数组使用的)错误经常出现。同时,我自己也会犯这些错误,因此有了这篇文章。让我们一起学习,以确保以后能正确地使用数组方法!

使用 Array.includes 替代 Array.indexOf

"如果需要在数组中查找某个元素,请使用 Array.indexOf。"

我记得在我学习 JavaScript 的课程中有类似的这么一句话。毫无疑问,这完全正确!

在 MDN 文档中,对 Array.indexOf 的描述是:返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。因此,如果在之后的代码中需要用到(给给定元素的)索引,那么 Array.indexOf 是不二之选。

然而,如果我们仅需要知道数组中是否包含给定元素呢?这意味着只是是与否的区别,这是一个布尔问题(boolean question)。针对这种情况,我建议使用直接返回布尔值的 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

使用 Array.find 替代 Array.filter

Array.filter 是一个十分有用的方法。它通过回调函数过滤原数组,并将过滤后的项作为新数组返回。正如它的名字所示,我们将这个方法用于过滤,(一般而言)会获得一个长度更短的新数组。

然而,如果知道经回调函数过滤后,只会剩余唯一的一项,那么我不建议使用 Array.filter。比如:使用等于某个唯一 ID 为过滤条件去过滤一个数组。在这个例子中,Array.filter 返回一个仅有一项的新数组。然而,我们仅仅是为了获取 ID 为特定 ID 的那一项,这个新数组显得毫无用处。

让我们讨论一下性能。为了获取所有符合回调函数过滤条件的项,Array.filter 必须遍历整个数组。如果原数组中有成千上万项,回调函数需要执行的次数是相当多的。
为避免这些情况,我建议使用 Array.find。它与 Array.filter 一样需要一个回调函数,(但只是返回)符合条件的第一项。当找到符合回调函数过滤条件的第一个元素时,它会立即停止往下的搜寻。不再遍历整个数组。

'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' }

使用 Array.some 替代 Array.find

我承认我经常犯这个错误。之后,一位朋友建议我去查看 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

译者注:补充一下 Array.some 与 Array.includes 使用上的区别。两者都返回一个布尔值,表示某项是否存在于数组之中,一旦找到对应的项,立即停止遍历数组。不同的是 Array.some 的参数是回调函数,而 Array.includes 的参数是一个值(均不考虑第二个可选参数)。

假设希望知道值为 value 的项是否存在于数组中,既可以编写代码:[].includes(value), 也可以给 Array.some 传入 item => item === value 作为回调函数。Array.includes  使用更简单,Array.some 可操控性更强。

使用 Array.reduce 替代 Array.filter 与 Array.map 的组合

事实上说,Array.reduce 不太容易理解。然而,如果我们先使用 Array.filter 过滤原数组,之后(对结果)再调用 Array.map (以获取一个新数组)。这看起似乎有点问题,是我们忽略了什么吗?

这样做的问题是:我们遍历了两次数组。第一次是过滤原数组以获取一个长度稍短的新数组,第二次遍历(译者注:指 Array.map)是对 Array.filter 的返回的新数组进行加工,再次创造了一个新数组!为得到最终的结果,我们结合使用了两个数组方法。每个方法都有它自己的回调函数,而且供 Array.map 使用的临时数组是由 Array.filter 提供的,(一般而言)该数组无法复用。

为避免如此低效场景的出现,我的建议是使用 Array.reduce 。一样的结果,更好的代码!Array.reduce 允许你将过滤后切加工过的项放进累加器中。累加器可以是需要待递增的数字、待填充的对象、 待拼接的字符串或数组等。

在上面的例子中,我们使用了 Array.map,(但更)建议使用累加器为待拼接数组的 Array.reduce 。在下面的例子中,根据变量 env 的值,我们会将它加进累加器中或保持累加器不变(即不作任何处理)。

'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'] }
// ]

总结

以上所述是小编给大家介绍的在 JavaScript 中更好地使用数组的相关知识,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
JQuery中form验证出错信息的查看方法
Oct 08 Javascript
关于JavaScript命名空间的一些心得
Jun 07 Javascript
JS实现点击复选框将按钮或文本框变为灰色不可用的方法
Aug 11 Javascript
详解JavaScript基于面向对象之创建对象(2)
Dec 10 Javascript
JS中作用域和变量提升(hoisting)的深入理解
Oct 31 Javascript
关于vue-router的beforeEach无限循环的问题解决
Sep 09 Javascript
JavaScript实现兼容IE6的收起折叠与展开效果实例
Sep 20 Javascript
在vue-cli项目中使用bootstrap的方法示例
Apr 21 Javascript
详解ajax的data参数错误导致页面崩溃
Apr 30 Javascript
小程序兼容安卓和IOS数据处理问题及坑
Sep 18 Javascript
JS实现音乐导航特效
Jan 06 Javascript
node.js基于dgram数据报模块创建UDP服务器和客户端操作示例
Feb 12 Javascript
axios 封装上传文件的请求方法
Sep 26 #Javascript
axios取消请求的实践记录分享
Sep 26 #Javascript
Node.js模拟发起http请求从异步转同步的5种用法
Sep 26 #Javascript
在vue中获取token,并将token写进header的方法
Sep 26 #Javascript
基于axios 解决跨域cookie丢失的问题
Sep 26 #Javascript
vue项目使用axios发送请求让ajax请求头部携带cookie的方法
Sep 26 #Javascript
基于JavaScript实现一个简单的Vue
Sep 26 #Javascript
You might like
PHP开启gzip页面压缩实例代码
2010/03/11 PHP
php checkbox复选框值的获取与checkbox默认值输出方法
2010/05/15 PHP
PHP的error_reporting错误级别变量对照表
2014/07/08 PHP
Zend Framework教程之Zend_Form组件实现表单提交并显示错误提示的方法
2016/03/21 PHP
Extjs学习笔记之二 初识Extjs之Form
2010/01/07 Javascript
jQuery AJAX 调用WebService实现代码
2010/03/24 Javascript
Array的push与unshift方法性能比较分析
2011/03/05 Javascript
jQuery数据缓存功能的实现思路及简单模拟
2013/05/27 Javascript
ff下JQuery无法监听input的keyup事件的解决方法
2013/12/12 Javascript
JavaScript框架(iframe)操作总结
2014/04/16 Javascript
Jquery节点遍历next与nextAll方法使用示例
2014/07/22 Javascript
Bootstrap入门书籍之(五)导航条、分页导航
2016/02/17 Javascript
微信公众号-获取用户信息(网页授权获取)实现步骤
2016/10/21 Javascript
vue.js项目nginx部署教程
2018/04/05 Javascript
在Vant的基础上封装下拉日期控件的代码示例
2018/12/05 Javascript
js实现选项卡效果
2020/03/07 Javascript
解决vue单页面应用进入页面加载所有 js 的问题
2020/08/12 Javascript
Python多维/嵌套字典数据无限遍历的实现
2016/11/04 Python
python使用pil进行图像处理(等比例压缩、裁剪)实例代码
2017/12/11 Python
pandas将DataFrame的列变成行索引的方法
2018/04/10 Python
浅析python3字符串格式化format()函数的简单用法
2018/12/07 Python
Python如何读写CSV文件
2020/08/13 Python
Auchan Direct波兰:欧尚在线杂货店
2016/10/19 全球购物
Probikekit欧盟:在线公路自行车专家
2019/07/12 全球购物
日本最大的购物网站:日本乐天市场(Rakuten Ichiba)
2020/11/04 全球购物
失业者真诚求职信范文
2013/12/25 职场文书
军训学生自我鉴定
2014/02/12 职场文书
竞选班干部演讲稿
2014/04/24 职场文书
暑期社会实践心得体会
2014/09/02 职场文书
传承焦裕禄精神思想汇报2014
2014/09/10 职场文书
教师党的群众路线教育实践活动学习笔记
2014/11/05 职场文书
房屋分割离婚协议书范本
2014/12/01 职场文书
关于应聘教师的自荐信
2016/01/28 职场文书
PyQt5爬取12306车票信息程序的实现
2021/05/14 Python
如何用PHP websocket实现网页实时聊天
2021/05/26 PHP
Win11电脑显示本地时间与服务器时间不一致怎么解决?
2022/04/05 数码科技