这是 JavaScript 初学者常见的误区。for_in 用于遍历对象中包括原型链上的全部可枚举的(enumerable)的 key,原本不是为遍历数组而存在。html
使用 for_in 遍历数组有三点问题:算法
JavaScript 引擎不保证对象的遍历顺序。当把数组做为普通对象遍历时一样不保证遍历出的索引顺序。编程
若是你改变了数组的原型对象(好比 polyfill)而没有将其设为 enumerable: false
,for_in 会把这些东西遍历出来。json
尽管理论上 JavaScript 使用对象的形式储存数组,JavaScript 引擎仍是会对数组这一很是经常使用的内置对象特别优化。 https://jsperf.com/for-in-vs-...
能够看到使用 for_in 遍历数组要比使用下标遍历数组慢 50 倍以上api
PS:你多是想找 for_of数组
有人使用 JSON 中深拷贝对象或数组。这虽然在多数状况是个简单方便的手段,但也可能引起未知 bug,由于:数据结构
null
NaN, undefined, Infinity 对于 JSON 中不支持的这些值,会在序列化 JSON 时被转换为 null,反序列化回来后天然也就是 nulljsp
JSON 序列化时会忽略值为 undefined 的 key,反序列化回来后天然也就丢失了编程语言
JSON 不支持对象类型,对于 JS 中 Date 对象的处理方式为转换为 ISO8601 格式的字符串。然而反序列化并不会把时间格式的字符串转化为 Date 对象函数式编程
做为原生函数,JSON.stringify
和 JSON.parse
自身操做 JSON 字符串的速度是很快的。然而为了深拷贝数组把对象序列化成 JSON 再反序列化回来彻底没有必要。
我花了一些时间写了一个简单的深拷贝数组或对象的函数,测试发现运行速度差很少是使用 JSON 中转的 6 倍左右,顺便还支持了 TypedArray、RegExp 的对象的复制
https://jsperf.com/deep-clone...
Array.prototype.find
是 ES2015 中新增的数组查找函数,与 Array.prototype.some
有类似之处,但不能替代后者。
Array.prototype.find
返回第一个符合条件的值,直接拿这个值作 if
判断是否存在,若是这个符合条件的值刚好是 0 怎么办?
arr.find
是找到数组中的值后对其进一步处理,通常用于对象数组的状况;arr.some
才是检查存在性;二者不可混用。
也是一个 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:不要手写排序算法了
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,做用是抛出一个异常。
咱们能够仿照这样的作法,来实现 arr.forEach
的 break
:
try { [1, 2, 3, 4, 5].forEach(x => { console.log(x); if (x === 3) { throw 'break'; } }); } catch (e) { if (e !== 'break') throw e; // 不要勿吞异常。。。 }
恶心的一B对不对。还有其余方法,好比用 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}`); }
for...of
和 forEach
的性能测试:https://jsperf.com/array-fore... Chrome 中 for...of
要快一些哦?
完