ES6-迭代器-iterator

92 阅读4分钟

前言

在javascript中有很多数据类型,字符串、数字、数组、对象等等,通常会对一些表示集合的数据类型进行遍历(迭代),比如遍历数组,那么遍历的方法也有很多,for循环,for in,for of,forEach,map,reduce等,通常不同的数据类型也会用不同的方法进行遍历,那么下面我也总结了ES6中关于迭代器 iterator 接口的一些内容。

for...in 和 for...of

两者都用于遍历,但是使用有明显的区别和不同的特点

for...in

其具有以下三个特征:

1. 循环返回的是对象的键名
2. 遍历数组返回的是下标
3. 不仅可以遍历显示具有的属性,还可以遍历到对象原型上的属性

对象:

let obj = {
    a:1
}

let obj2 = Object.create(obj)//必须放参数
obj2.aa = 100
obj2.bb = 200

for(let key in obj2){
    console.log(key); //aa bb a
}

数组:

Array.prototype.c = 'c'
const arr = ['a','b']

for(let key in arr){
    console.log(key); //0 1 c
}

当然我们可以通过一些方法判断该对象或者数组是否具有该属性,比如hasOwnProperty(),这样就可以只操作对象或数组本身具有的,就不会返回对象原型上的属性:

let obj = {
    a:1
}

let obj2 = Object.create(obj)//必须放参数
obj2.aa = 100
obj2.bb = 200

for(let key in obj2){
    //判断该key是否为obj2显示具有的属性
    if(obj2.hasOwnProperty(key)){
        console.log(key);
    }
}

for...of

其具有以下三个特征:

1. 遍历数组返回的是元素
2. 不能遍历不具有 iterator 属性的数据结构
3. 不能遍历到数组原型上的属性

数组是天生具有可迭代性的,所以可以用 for...of 遍历:

const arr = ['a', 'b', 'c', 'd']

for(let item of arr){
    console.log(item); //a b c d
}

但是对于直接定义的对象,其不具有可迭代属性,所以无法被 for...of 遍历,会报错:

let obj = {
    a:1,
    b:2,
    c:3
}
for(let item of obj){//error
    console.log(item);
}

image.png

iterator 接口

Iterator 接口,就是为所有数据结构,提供了一种统一的访问机制,即for...of循环。当使用for...of循环遍历某种数据结构时,该循环会自动去寻找 Iterator 接口。

一种数据结构只要部署了 Iterator 接口,我们就称这种数据结构是“可遍历的”(iterable)

默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)。Symbol.iterator属性本身是一个函数,执行这个函数,就会返回一个遍历器对象,这个遍历器中隐式具有一个next()方法,用于遍历。

image.png

Iterator 的遍历过程:

1. 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。

2. 第一次调用指针对象的`next`方法,可以将指针指向数据结构的第一个成员。

3. 第二次调用指针对象的`next`方法,指针就指向数据结构的第二个成员。

4. 不断调用指针对象的`next`方法,直到它指向数据结构的结束位置。

每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含valuedone两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束,当值为true即为遍历结束。

image.png

考点

考点1:手写实现一个遍历方法,操作数组每一项:

let arr = [1,2,3]

function each(arr){
    let res = []
    let it = arr[Symbol.iterator]()  //返回一个迭代器对象

    function deep(){ //利用递归实现循环多次调用遍历器对象上的next()
        let current = it.next() 
        if(current.done){ //调用next()得到该项的done的值为true表示遍历结束,则直接返回
            return
        }
        res.push(current.value * 2) // done为false表示遍历未结束,将该项的value添加入新数组
        deep() //递归,继续遍历
    }
    deep()
    return res
}

let newArr = each(arr)
console.log(newArr); //[2,4,6]

考点2:如何让下面代码成立并成功解构(字节面试题)

let [a,b] = {a:1,b:2}

实现思路:

1.之所以不能实现解构,是因为对象不可迭代性
2.对象不具有 Symbol.iterator 属性,所以不具有可迭代性,那么可以在对象原型上添加 Symbol.iterator 属性,该属性是一个函数,并返回一个迭代器对象
3.可以想到 let [a,b] = [1,2] ,这里可以成功结构是因为数组可迭代性,所以我们可以将对象的值提取转换成数组,并返回这个数组的迭代器对象,那么对象就具有可迭代性,实现解构

代码:

Object.prototype[Symbol.iterator] = function(){
    //返回一个Array类型的可迭代对象
    return Object.values(this)[Symbol.iterator]() //Object作为构造函数,this指向实例对象
}

let [a,b] = {a:1,b:2}
console.log(a,b); //1 2

总结

for...of是ES6创立的一个新的循环方式,利用数据结构的可迭代性,iterator 接口也主要是为了for...of提供,在平时开发也可能经常使用这种循环方式,希望以上内容可以帮助大家更加了解迭代器