在JavaScript刚刚开始萌生的时候,我们通常使用这种方式遍历数组
let arr1 = [1, 2, 3, 4];
for (let index = 0; index < arr1.length; index++) {
const element = arr1[index];
console.log(element); // 1 2 3 4
}
自从ES5 正式发布之后,我们可以使用 forEach 方法来遍历数组.
let arr2 = [1, 2, 3, 4];
arr2.forEach( item => {
if (item === 3) return
console.log(item); // 1 2 4
})
- 显然的,这段代码看起来更加简洁,但是并没有按照我们预期的进行终结遍历。
- 在文档中我们可以看到描述
forEach() 方法对数组的每个元素执行一次提供的函数。
意思就是即使你在某个函数中return
了也只是终结了当次函数的执行。
那么,你一定想尝试一下 for-in 循环
let arr3 = [1, 2, 3, 4];
for (const index in arr3) {
if (index === 3) break
console.log(index); // 0 1 2 3
console.log(typeof index); // string
}
程序再次没有按照我们预期的运行:
在这段代码中,赋值给index的值并不是实际的数字,可以看到输出的是string '0' '1' '2' '3', 因此如果在这之前你并没深入理解,可能会掉进坑里,此时我们可以把 === 改成 == 即可
for...in 循环只遍历可枚举属性。像Array和Object使用内置构造函数所创建的对象都会继承自Object.prototype 和 String.prototype的不可枚举属性, 如果在原型上自定义了属性,那么稍微不注意将会导致如下错误:
let arr3 = [1, 2, 3, 4];
Array.prototype.name = '看看我被遍历了没';
for (const key in arr3) {
// 将额外的遍历属性
// const element = arr3[key];
// console.log(element); // 1 2 3 4 '看看我被遍历没'
// 正确做法
if (arr3.hasOwnProperty(key)) {
const element = arr3[key];
console.log(element); // 1 2 3 4
}
}
更加可怕的是,由于迭代的顺序是依赖于执行环境的,所以数组遍历不一定按次序访问元素。
简而言之, for-in 是为普通对象设计的,你可以遍历得到字符串类型的键,因此不适用于数组遍历。
更好的选择 for-of 循环
在ES6中增加了一种新的循环语法来解决目前的问题:
for (variable of iterable) {
//statements
}
for...of 可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句。
这是最简洁、最直接的遍历数组元素的语法
这个方法避开了 for-in 循环的所有缺陷
与forEach不同的是,它可以正确响应 break、continue 和 return 语句
未来的 JS 可以使用一些新型的集合类,甚至会有更多的类型
陆续诞生,而 for-of 就是为遍历所有这些集合特别设计的循环语句for-of 循环不支持普通对象,但如果你想迭代一个对象的属性,你可以用 for-in 循环(这也是它的本职工作)或内建的 Object.keys()方法:
// 向控制台输出对象的可枚举属性
for (var key of Object.keys(someObject)) {
console.log(key + ": " + someObject[key]);
}
深入理解
“能工摹形,巧匠窃意.” -- 巴勃罗·毕加索
ES6始终坚信这样的宗旨: 凡是新加入的特性,势必已在其他语言中得到强有力的实用性证明。
举个例子,新加入的for-of 循环像极了C++、Java、C#以及Python中的循环语句。与他们一样,这里的 for-of 循环支持语言和标准库中提供的几种不同的数据结构,它同样也是这门语言中的一个扩展点。