分析JavaScript数组操作难点


Posted in Javascript onDecember 18, 2017

以下内容是学习JavaScript数组的时候总结的经验以及需要注意的点。

不要用 for_in 遍历数组

这是 JavaScript 初学者常见的误区。for_in 用于遍历对象中包括原型链上的所有可枚举的(enumerable)的 key,本来不是为遍历数组而存在。

使用 for_in 遍历数组有三点问题:

1、遍历顺序不固定

JavaScript 引擎不保证对象的遍历顺序。当把数组作为普通对象遍历时同样不保证遍历出的索引顺序。

2、会遍历出对象原型链上的值。

如果你改变了数组的原型对象(比如 polyfill)而没有将其设为 enumerable: false,for_in 会把这些东西遍历出来。

3、运行效率低下。

尽管理论上 JavaScript 使用对象的形式储存数组,JavaScript 引擎还是会对数组这一非常常用的内置对象特别优化。 https://jsperf.com/for-in-vs-...
可以看到使用 for_in 遍历数组要比使用下标遍历数组慢 50 倍以上

PS:你可能是想找 for_of

不要用 JSON.parse(JSON.stringify()) 深拷贝数组

有人使用 JSON 中深拷贝对象或数组。这虽然在多数情况是个简单方便的手段,但也可能引发未知 bug,因为:会使某些特定值转换为 null

NaN, undefined, Infinity 对于 JSON 中不支持的这些值,会在序列化 JSON 时被转换为 null,反序列化回来后自然也就是 null

会丢失值为 undefined 的键值对

JSON 序列化时会忽略值为 undefined 的 key,反序列化回来后自然也就丢失了

会将 Date 对象转换为字符串

JSON 不支持对象类型,对于 JS 中 Date 对象的处理方式为转换为 ISO8601 格式的字符串。然而反序列化并不会把时间格式的字符串转化为 Date 对象

运行效率低下。

作为原生函数,JSON.stringifyJSON.parse 自身操作 JSON 字符串的速度是很快的。然而为了深拷贝数组把对象序列化成 JSON 再反序列化回来完全没有必要。

我花了一些时间写了一个简单的深拷贝数组或对象的函数,测试发现运行速度差不多是使用 JSON 中转的 6 倍左右,顺便还支持了 TypedArray、RegExp 的对象的复制

https://jsperf.com/deep-clone...

不要用 arr.find 代替 arr.some

Array.prototype.find 是 ES2015 中新增的数组查找函数,与 Array.prototype.some 有相似之处,但不能替代后者。

Array.prototype.find 返回第一个符合条件的值,直接拿这个值做 if 判断是否存在,如果这个符合条件的值恰好是 0 怎么办?

arr.find 是找到数组中的值后对其进一步处理,一般用于对象数组的情况;arr.some 才是检查存在性;两者不可混用。

不要用 arr.map 代替 arr.forEach

也是一个 JavaScript 初学者常常犯的错误,他们往往并没有分清 Array.prototype.mapArray.prototype.forEach 的实际含义。

map 中文叫做 映射,它通过将某个序列依次执行某个函数导出另一个新的序列。这个函数通常是不含副作用的,更不会修改原始的数组(所谓纯函数)。

forEach 就没有那么多说法,它就是简单的把数组中所有项都用某个函数处理一遍。由于 forEach 没有返回值(返回 undefined),所以它的回调函数通常是包含副作用的,否则这个 forEach 写了毫无意义。

确实 mapforEach 更加强大,但是 map 会创建一个新的数组,占用内存。如果你不用 map 的返回值,那你就应当使用 forEach

补:心得补充

ES6 以前,遍历数组主要就是两种方法:手写循环用下标迭代,使用 Array.prototype.forEach。前者万能,效率最高,可就是写起来比较繁琐——它不能直接获取到数组中的值。

笔者个人是喜欢后者的:可以直接获取到迭代的下标和值,而且函数式风格(注意 FP 注重的是不可变数据结构,forEach 天生为副作用存在,所以只有 FP 的形而没有神)写起来爽快无比。但是!不知各位同学注意过没有:forEach 一旦开始就停不下来了。。。

forEach 接受一个回调函数,你可以提前 return,相当于手写循环中的 continue。但是你不能 break——因为回调函数中没有循环让你去 break

[1, 2, 3, 4, 5].forEach(x => {
 console.log(x);
 if (x === 3) {
  break; // SyntaxError: Illegal break statement
 }
});

解决方案还是有的。其他函数式编程语言例如 scala 就遇到了类似问题,它提供了一个函数
break,作用是抛出一个异常。

分析JavaScript数组操作难点

我们可以仿照这样的做法,来实现 arr.forEachbreak

try {
 [1, 2, 3, 4, 5].forEach(x => {
  console.log(x);
  if (x === 3) {
   throw 'break';
  }
 });
} catch (e) {
 if (e !== 'break') throw e; // 不要勿吞异常。。。
}

还有其他方法,比如用 Array.prototype.some 代替 Array.prototype.forEach

考虑 Array.prototype.some 的特性,当 some 找到一个符合条件的值(回调函数返回 true)时会立即终止循环,利用这样的特性可以模拟 break

[1, 2, 3, 4, 5].some(x => {
 console.log(x);
 if (x === 3) {
  return true; // break
 }
 // return undefined; 相当于 false
});

some 的返回值被忽略掉了,它已经脱离了判断数组中是否有元素符合给出的条件这一原始的含义。

在 ES6 前,笔者主要使用该法(其实因为 Babel 代码膨胀的缘故,现在也偶尔使用),ES6 不一样了,我们有了 for...of。for...of 是真正的循环,可以 break

for (const x of [1, 2, 3, 4, 5]) {
 console.log(x);
 if (x === 3) {
  break;
 }
}

但是有个问题,for...of 似乎拿不到循环的下标。其实 JavaScript 语言制定者想到了这个问题,可以如下解决:

for (const [index, value] of [1, 2, 3, 4, 5].entries()) {
 console.log(`arr[${index}] = ${value}`);
}

Array.prototype.entries

for...offorEach 的性能测试:https://jsperf.com/array-fore... Chrome 中 for...of 要快一些哦?

如果有更多建议欢迎留言指出

Javascript 相关文章推荐
高亮显示web页表格行的javascript代码
Nov 19 Javascript
Jquery中的CheckBox、RadioButton、DropDownList的取值赋值实现代码
Oct 12 Javascript
JavaScript中判断函数、变量是否存在
Jun 10 Javascript
详解JavaScript的流程控制语句
Nov 30 Javascript
JS在一定时间内跳转页面及各种刷新页面的实现方法
May 26 Javascript
JavaScript Ajax编程 应用篇
Jul 02 Javascript
jQuery Easyui Tabs扩展根据自定义属性打开页签
Aug 15 Javascript
微信和qq时间格式模板实例详解
Oct 21 Javascript
解决vue-cli项目webpack打包后iconfont文件路径的问题
Sep 01 Javascript
vue项目中,main.js,App.vue,index.html的调用方法
Sep 20 Javascript
JS中队列和双端队列实现及应用详解
Sep 29 Javascript
JS数据类型分类及常用判断方法
Nov 19 Javascript
微信小程序基于本地缓存实现点赞功能的方法
Dec 18 #Javascript
JavaScript模块详解
Dec 18 #Javascript
webpack打包js文件及部署的实现方法
Dec 18 #Javascript
react+redux的升级版todoList的实现
Dec 18 #Javascript
总结js中的一些兼容性易错的问题
Dec 18 #Javascript
详解最新vue-cli 2.9.1的webpack存在问题
Dec 16 #Javascript
HTML5+JS+JQuery+ECharts实现异步加载问题
Dec 16 #jQuery
You might like
php empty() 检查一个变量是否为空
2011/11/10 PHP
PHP语法自动检查的Vim插件
2014/08/11 PHP
php输出指定时间以前时间格式的方法
2015/03/21 PHP
PHP中4种常用的抓取网络数据方法
2015/06/04 PHP
对php 判断http还是https,以及获得当前url的方法详解
2019/01/15 PHP
PHP从零开始打造自己的MVC框架之路由类实现方法分析
2019/06/03 PHP
一个js写的日历(代码部分网摘)
2009/09/20 Javascript
类似php的js数组的in_array函数自定义方法
2013/12/27 Javascript
node.js中的fs.chmodSync方法使用说明
2014/12/18 Javascript
微信小程序 picker-view 组件详解及简单实例
2017/01/10 Javascript
Bootstrap中glyphicons-halflings-regular.woff字体报404错notfound的解决方法
2017/01/19 Javascript
轻松理解vue的双向数据绑定问题
2017/10/30 Javascript
微信小游戏之使用three.js 绘制一个旋转的三角形
2019/06/10 Javascript
jQuery实现消息弹出框效果
2019/12/10 jQuery
three.js着色器材质的内置变量示例详解
2020/08/16 Javascript
vscode中的vue项目报错Property ‘xxx‘ does not exist on type ‘CombinedVueInstance<{ readyOnly...Vetur(2339)
2020/09/11 Javascript
[49:27]2018DOTA2亚洲邀请赛 4.4 淘汰赛 TNC vs VG 第一场
2018/04/05 DOTA
Python实现打印螺旋矩阵功能的方法
2017/11/21 Python
python Celery定时任务的示例
2018/03/13 Python
django与小程序实现登录验证功能的示例代码
2019/02/19 Python
python机器人运动范围问题的解答
2019/04/29 Python
python中通过selenium简单操作及元素定位知识点总结
2019/09/10 Python
pytorch方法测试详解——归一化(BatchNorm2d)
2020/01/15 Python
python标准库sys和OS的函数使用方法与实例详解
2020/02/12 Python
python对数组进行排序,并输出排序后对应的索引值方式
2020/02/28 Python
pycharm 2020 1.1的安装流程
2020/09/29 Python
Casadei卡萨蒂官网:意大利奢侈鞋履品牌
2017/10/28 全球购物
维也纳通行证:Vienna PASS
2019/07/18 全球购物
专家推荐信模板
2014/05/09 职场文书
职业道德模范事迹材料
2014/08/24 职场文书
学习优秀党务工作者先进事迹材料思想报告
2014/09/17 职场文书
秦始皇兵马俑导游词
2015/02/02 职场文书
2015年学校远程教育工作总结
2015/07/20 职场文书
python批量创建变量并赋值操作
2021/06/03 Python
Linux下搭建SFTP服务器的命令详解
2022/06/25 Servers
table不让td文字溢出操作方法
2022/12/24 HTML / CSS