JavaScript 遍历

最传统的 for 循环

关键在: 终止条件是遍历开始前确定的, 还是在每次遍历中去计算值的.

1. 遍历开始前就确定了终止条件

1
2
3
4
5
6
7
const arr = [1, 2], len = arr.length;
let insertTimes = 0;
for (let i = 0; i < len; i++) {
console.log(arr[i]);
if (insertTimes++ < 5) arr.push('insert');
}
// 输出: 1, 2

2. 每次遍历判定是否终止时, 获取终止条件

1
2
3
4
5
6
7
const arr = [1, 2];
let insertTimes = 0;
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
if (insertTimes++ < 5) arr.push('insert');
}
// 输出: 1, 2, insert, insert, insert, insert, insert

Array.forEach

遍历开始前就确定了遍历次数

1
2
3
4
5
6
7
8
9
const arr = [1, 2];
let insertTimes = 0;
arr.forEach(item => {
console.log(item);
if (insertTimes++ < 5) arr.push('insert');
})
// 输出: 1, 2
console.log(arr);
// 输出: [1, 2, 'insert', 'insert']

传给 callback 的值是执行那一刻的值, 在 callback 执行中进行的修改只能影响之后的遍历

1
2
3
4
5
6
7
8
9
10
// 前一次遍历的 insert 影响下一次遍历的取值
const arr = [1, 2];
let insertTimes = 0;
arr.forEach((item, index) => {
console.log(item);
if (insertTimes++ < 5) arr.unshift('insert');
})
// 输出: 1, 1
console.log(arr);
// 输出: ['insert', 'insert', 1, 2]
1
2
3
4
5
6
7
8
9
10
// 更改当前遍历项的值无法在遍历当次体现
const arr = [1, 2];
let insertTimes = 0;
arr.forEach((item, index) => {
arr[index] = 'change';
console.log(item);
})
// 输出: 1, 2
console.log(arr);
// 输出: ['change', 'change']

for … of …

实时判定遍历是否终止

1
2
3
4
5
6
7
const arr = [1, 2];
let insertTimes = 0;
for (let item of arr) {
console.log(item);
if (insertTimes++ < 5) arr.push('insert');
}
// 输出: 1, 2, insert, insert, insert, insert, insert

每次遍历前都获取最新的数组, 进入遍历后则不受数组改变影响

1
2
3
4
5
6
7
const arr = [1, 2];
let insertTimes = 0;
for (let item of arr) {
console.log(item);
if (insertTimes++ < 5) arr.unshift('insert');
}
// 输出: 1, 1, 1, 1, 1, 1, 2
1
2
3
4
5
6
7
8
9
10
const arr = [1, 2];
let changeIndex = 0;
arr.custom = 'custom';
for (let item of arr) {
arr[changeIndex++] = 'change';
console.log(item);
}
// 输出: 1, 2
console.log(arr);
// 输出: ['change', 'change', custom: 'custom']

“for of” 原理: 基于遍历对象具有的 Symbol.iterator 属性.
推测: 自定义属性不在迭代器的范围里, 因此 “for of” 不能遍历出自定义属性.

for … in …

遍历开始前就确定了遍历次数

1
2
3
4
5
6
7
const arr = [1, 2];
let insertTimes = 0;
for (let i in arr) {
console.log(arr[i]);
if (insertTimes++ < 5) arr.push('insert');
}
// 输出: 1, 2

遍历对象遭到实时读写

1
2
3
4
5
6
7
const arr = [1, 2];
let insertTimes = 0;
for (let i in arr) {
console.log(arr[i]);
if (insertTimes++ < 5) arr.unshift('insert');
}
// 输出: 1, 1
1
2
3
4
5
6
7
8
9
const arr = [1, 2];
arr.custom = 'custom';
for (let i in arr) {
arr[i] = 'change';
console.log(arr[i]);
}
// 输出: change, change, change
console.log(arr);
// 输出: ['change', 'change', custom: 'change']

“for in” 原理: 取出对象的所有属性名进行遍历, 所以可遍历出自定义属性.
另外, 对于没有值的属性不会取出 (遍历数组时与基础 for 循环的差别) (forEach 也有这个过滤空值的特性), 所以遍历稀疏数组会自动跳过无值的位置.