当前位置: 首页 > 后端技术 > Node.js

初学者:注意JavaScript中的数组操作

时间:2023-04-03 13:44:49 Node.js

不要使用for_in遍历数组。这是JavaScript初学者的一个常见误解。for_in用于遍历对象中包括原型链在内的所有可枚举(enumerable)键,对于遍历数组是不存在的。使用for_in遍历数组存在三个问题:遍历顺序不固定JavaScript引擎不保证对象的遍历顺序。作为普通对象遍历数组时,遍历的索引顺序也是不保证的。将遍历对象原型链上的值。如果你改变了数组的原型对象(比如一个polyfill)而没有将它设置为enumerable:false,for_in将迭代这些东西。运行效率低下。尽管JavaScript理论上将数组存储为对象,但JavaScript引擎针对数组(一种非常常用的内置对象)进行了特别优化。https://jsperf.com/for-in-vs-...可以看到使用for_in遍历数组比使用下标遍历数组慢了50多倍PS:你可能找的是for_of而不是使用JSON.parse(JSON.stringify())深度复制数组有些人在JSON中使用对象或数组的深度复制。虽然这在大多数情况下是一种简单方便的方法,但它也可能会导致未知的错误,因为:它会将一些特定的值转换为nullNaN、undefined、Infinity对于这些JSON中不支持的值,他们在序列化JSON的时候会被转换为null,反序列化之后,自然会失去undefined的key-value值。JSON序列化时,值为undefined的key会被忽略,反序列化后会丢失。Date对象将被转换为字符。StringJSON不支持对象类型,JS中对Date对象的处理方式是将其转为ISO8601格式的字符串。但是,反序列化不会将时间格式的字符串转换为Date对象,效率低下。作为本机函数,JSON.stringify和JSON.parse可以快速处理JSON字符串。但是,完全没有必要将对象序列化成JSON再反序列化回深拷贝数组。我花了一些时间编写了一个用于深度复制数组或对象的简单函数。测试发现运行速度比使用JSON传输快6倍左右。对了,它也支持复制TypedArray和RegExp对象https://jsperf.com/deep-clone...不要用arr.find代替arr.someArray.prototype.find是新增的数组搜索功能在ES2015中。它类似于Array.prototype.some,但不能替代它。Array.prototype.find返回第一个满足条件的值,直接将这个值作为if判断是否存在。如果满足条件的值恰好为0怎么办?arr.find是在数组中查找值并进一步处理,一般用于对象数组的情况;arr.some是检查是否存在;两者不能混为一谈。不要用arr.map代替arr.forEach也是JavaScript初学者常犯的错误,他们往往分不清Array.prototype.map和Array.prototype.forEach的实际含义。map中文叫做映射,通过顺序执行某个函数,派生出另一个新的序列。这个函数通常没有副作用,更不用说修改原始数组了(所谓的纯函数)。forEach就不用说那么多了,它只是简单地用某个函数处理数组中的所有项。由于forEach没有返回值(返回undefined),它的回调函数通常会有副作用,否则这个forEach是没有意义的。map确实比forEach强大,但是map会新建数组,占用内存。如果不用map的返回值,那就用forEach来弥补:数组排序的时候,永远记得传入比较函数arr.sort。默认情况下,数组中的所有元素将被转换为字符串并按字典顺序排序。例如:[1,2,3,4,5,6,7,8,9,10].sort()//=>[1,10,2,3,4,5,6,7,8,9]除非您正在对字符串数组进行排序,否则请将比较函数传递给它。PS:排序算法不要手写补充:forEach和breakES6之前遍历数组主要有两种方法:手写循环使用下标迭代,使用Array.prototype.forEach。前者通用,效率最高,但写起来比较麻烦——不能直接获取数组中的值。笔者个人比较喜欢后者:可以直接获取迭代的下标和值,函数式风格(注意FP侧重于不可变数据结构,forEach本质上是一个副作用,所以只有FP的形式没有精神)写的很爽快。但!不知道大家有没有注意到:forEach一旦启动,就停不下来了。..forEach接受一个回调函数,可以提前返回,相当于在手写循环中继续。但是你不能中断-因为回调函数中没有循环让你中断:[1,2,3,4,5].forEach(x=>{console.log(x);if(x===3){break;//SyntaxError:非法的break语句}});还是有办法的。scala等其他函数式编程语言也遇到过类似的问题。它提供了一个函数break,用于抛出异常。我们可以按照这种方式来实现arr.forEach的中断:try{[1,2,3,4,5].forEach(x=>{console.log(x);if(x===3){抛出'break';}});}catch(e){if(e!=='break')抛出e;//不要抛出异常。..}令人厌恶的1B,对吧?还有其他方法,比如使用Array.prototype.some代替Array.prototype.forEach。考虑Array.prototype.some的特性。当找到一个符合条件的值时(回调函数返回true),循环会立即终止。利用这样的特性可以模拟break:[1,2,3,4,5].some(x=>{console.log(x);if(x===3){returntrue;//break}//返回未定义;相当于false});some的返回值被忽略,已经脱离了判断数组中是否有满足给定条件的元素的本意。在ES6之前,笔者主要使用这种方式(其实是因为Babel代码的扩展,现在偶尔会用到),ES6不同,我们有for...of。for...of是一个真正的循环,可以中断:for(constxof[1,2,3,4,5]){console.log(x);如果(x===3){中断;但是有个问题,for...of好像是获取不到循环的下标。其实JavaScript语言的开发者已经想到了这个问题,可以这样解决:for(const[index,value]of[1,2,3,4,5].entries()){console.log(`arr[${index}]=${value}`);}Array.prototype.entriesfor...of和forEach性能测试:https://jsperf.com/array-fore...For...在Chrome中的更快哦?如果您有更多建议,欢迎留言指出