這還要從一道面試題說起,請問下面兩種情況分別打印什麼:
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 <anonymous>: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接口的數組。