# 了解一下各种数组遍历的差异
# 简单测试一下
const n = 1000000
const arr = new Array(n).fill(0)
console.log(n, '条数据遍历:')
// 普通for循环
let start = Date.now()
for (let i = 0; i < arr.length; i++) {}
console.log('普通for:', Date.now() - start, 'ms')
// 加强for循环
start = Date.now()
for (let i = 0, j = arr.length; i < j; i++) {}
console.log('加强for:', Date.now() - start, 'ms')
// 倒序for循环
start = Date.now()
for (let i = arr.length; i--; ) {}
console.log('倒序for:', Date.now() - start, 'ms')
// forEach
start = Date.now()
arr.forEach(() => {})
console.log('forEach:', Date.now() - start, 'ms')
// map
start = Date.now()
arr.map(() => {})
console.log('map:', Date.now() - start, 'ms')
// for in
start = Date.now()
for (let i in arr) {
}
console.log('for in:', Date.now() - start, 'ms')
// for of
start = Date.now()
for (let i of arr) {
}
console.log('for of:', Date.now() - start, 'ms')
// for-in是最慢的,倒序for是最快的
// 经简单测试:10万条数组相差时间不大,100万条以上时间差距明显
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
10 万条数据 | 100 万条数据 | 1000 万条数据 | |
---|---|---|---|
普通 for | 2ms | 3ms | 7ms |
加强 for | 1ms | 2ms | 7ms |
倒序 for | 0ms | 3ms | 7ms |
forEach | 2ms | 11ms | 106ms |
map | 4ms | 19ms | 192ms |
for in | 11ms | 166ms | 2668ms |
for of | 6ms | 40ms | 159ms |
# 进一步分析
# 加强 for
将persons.length
缓存到变量len
中,这样每次循环时就不会再读取数组的长度。
# 倒序 for
从后向前,直到 i === 0 为止。这种方式不仅去除了每次循环中读取数组长度的操作,而且只创建了一个变量 i。
# forEach
forEach
方法对数组的每个元素执行一次提供的 callback 函数,forEach 是一个数组方法,可以用来把一个函数套用在一个数组中的每个元素上,forEach
为每个数组元素执行 callback 函数只可用于数组,遍历一个数组让数组每个元素做一件事情
那些已删除(使用 delete 方法等情况)或者**未初始化的项(如 empty)**将被跳过(但不包括那些值为 undefined 的项)(例如在稀疏数组上);
不像 map() 或者 reduce() ,它总是返回 undefined 值,并且不可链式调用。典型用例是在一个链的最后执行副作用。
# map
map
方法会给原数组中的每个元素都按顺序调用一次 callback 函数。callback 每次执行后的返回值(包括 undefined)组合起来形成一个新数组。
callback 函数只会在有值的索引上被调用;那些从来没被赋过值或者使用 delete 删除的索引则不会被调用。让数组通过某种计算产生一个新数组,影射成一个新的数组
# for in
一般会使用for-in
来遍历对象的属性的,不过属性需要 enumerable
,才能被读取到. for-in
循环只遍历可枚举属性。
一般常用来遍历对象,包括非整数类型的名称和继承的那些原型链上面的属性也能被遍历。
像 Array 和 Object 使用内置构造函数所创建的对象都会继承自 Object.prototype 和 String.prototype 的不可枚举属性就不能遍历了.
# for of
for-of
语句在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句。
只要是一个 iterable 的对象,就可以通过for-of
来迭代.
# for-of 和 for-in 的区别
for-in
语句以原始插入顺序迭代对象的可枚举属性。for-in
会把继承链的对象属性都会遍历一遍,所以会更花时间.
for-of
语句只遍历可迭代对象的数据。
# 浏览器差异
forEach
、map
、for in
和for of
这些ES6+
的语法并没有传统的for
循环或者while
循环快,特别是map
方法。
for循环 while循环 for of 循环
是可以通过break
关键字跳出的,而forEach map
这种循环是无法跳出的。
但是随着执行环境和浏览器的不同,这些语法在执行速度上也会出现偏差甚至反转的情况
- 谷歌浏览器中
ES6+
的循环语法会普遍比传统的循环语法慢,但是火狐和 safari 中情况却几乎相反。 - 谷歌浏览器的各种循环语法的执行耗时上差距并不大。但
map
特殊,速度明显比其他几种语法慢,而在火狐和 safari 中却出现了反转,map
反而比较快! - 苹果大法好
# 参考
JavaScript 数组遍历方法的对比 (opens new window)
JS 数组循环的性能和效率分析(for、while、forEach、map、for of) (opens new window)