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万+

猜你喜欢

转载自blog.csdn.net/dongguan_123/article/details/103650852