for...in和for...of的區別和使用場景

這還要從一道面試題說起,請問下面兩種情況分別打印什麼:

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接口的數組。

發佈了59 篇原創文章 · 獲贊 29 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章