这还要从一道面试题说起,请问下面两种情况分别打印什么:
let arr = [1,2,3,4,5]
for(let i in arr) {
console.log(i) // 0,1,2,3,4
}
for(let i of arr) {
console.log(i) // 1,2,3,4,5
}
可以看到,for in遍历的是数组索引,也是数组的键,for of遍历的是数组值,再看下面的例子
let arrObj1 = {
a:1,
b:2,
c:3
}
for(let i in arrObj1) {
console.log(i) // a b c
}
for(let i of arrObj1) {
console.log(i) // Uncaught TypeError: arrObj1 is not iterable at :1:14
}
for in遍历键,输出a b c,for of报错了,当前对象不可遍历
到这里我们大致可以得出一些结论了,就是:
for in即可以遍历数组,也可以遍历对象
for of只能遍历数组
继续研究一下for in
let bb = [1,2,3]
bb.__proto__.name = 'tttt'
for(let i in bb) {
console.log(i) // 0 1 2 name
}
可以看到,for in还会遍历数组原型上的属性和方法
加之for in遍历数组时,顺序不可控,可能并不是看到的书序,所以for in不适合拿来遍历数组,更适合遍历对象
for of不只能遍历普通对象,它本质上是可以遍历部署了iterator接口的类数组对象
那么什么是iterator呢?
遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)
iterator接口其实是一个方法,它挂载于数组原型下Symbol(Symbol.iterator),主要是供for of方法消费的。
当使用for of方法访问数组的时候,其实是调用了数组原型下的Symbol(Symbol.iterator)方法,方法内部返回一个next()方法,类似于指针,每当i增加1,都会调用一下next()并且返回一个属性对象,包含value和done,value是当前值,done标识是否遍历完成。
let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator]();
iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }
除了普通的数组部署了iterator接口外,好多类数组对象也部署了该遍历器接口,比如arguments、NodeList、String、Map、Set都部署了,所以它们都是可以通过for of遍历的
在解构赋值和扩展运算符的时候其实也是调用了遍历器接口,将返回的value值重新处理
let a = 'abc';
[...a] // ['a','b','c']
上面说了,普通对象使用for of遍历时会报错,那是因为普通对象下么有部署遍历器接口,我们可以手动部署一个
let myObj = {
data: ['a','b','c'],
[Symbol.iterator]() {
const self = this;
let index = 0;
return {
next() {
if(index < self.data.length) {
return {
value:self.data[index++],
done: false
}
}else{
return {value:undefined,done:true}
}
}
}
}
}
for(let i of myObj) {
console.log(i) // a b c
}
或者更具普适性的
let obj2 = {a: 1,b:2}
obj2[Symbol.iterator] = function* () {
for(const [key,val] of Object.entries(this)) {
yield {
key,val
}
}
}
for(let i of obj2) {
console.log(i) // {key: "a", val: 1},{key: "b", val: 2}
}
所以for in一般用来遍历对象,for of用来遍历数组或者严格来说,是部署了iterator接口的数组。