什么是迭代器?什么是可迭代对象?为什么数组可以用for of遍历?

203 阅读3分钟

 迭代器iterator

1.1. 什么是迭代器,创建迭代器

  • 迭代器本身其实就是一个对象
  • 要想创建迭代器,就需要这个对象中有一个next方法,并且next方法会返回一个对象,对象中有done和value属性
    • 如果还有数据没有遍历完,done的值就是 false,否则为 true
  • 返回{done: false/true, value: value/undefined}
const arrs = ["abc", "cba", "nba"]
const friends = ["kobe", "james", "linda", "Judy"]

function createIterator(arr) {
    let index = 0
    // 迭代器
    return {
        next: function() {
            if(index < arr.length) {
                return { done: false, value: arr[index++] }
            } else {
                return { done: true }
            }
        }
    }
}

const iterator1 = createIterator(arrs)
console.log(iterator1.next())
console.log(iterator1.next())
console.log(iterator1.next())
console.log(iterator1.next())

const iterator2 = createIterator(friends)
console.log(iterator2.next())
console.log(iterator2.next())
console.log(iterator2.next())
console.log(iterator2.next())
console.log(iterator2.next())

1.2. 什么是可迭代对象

  • 当一个对象实现了**可迭代协议(iterator protocol)**时,它就是一个可迭代对象
  • 意思就是这个对象中必须实现@iterator方法,也就是在代码中写的**[Symbol.interator]**方法
  • [Symbol.interator]方法必须返回一个迭代器
  • 当一个对象称为可迭代对象之后,就可以使用比如for...of这样的操作
const obj = {
    name: "Judy",
    age: 18,
    height: 1.88,
    friends: ["kobe", "james", "coderWhy", "coderKing"],

    // 想成为一个可迭代对象,就必须实现下面的方法
    // 想要for...of遍历时,遍历出来什么东西,是由我说了算的
    [Symbol.iterator]: function() {
        let index = 0
        // 此时,迭代出来的是所有的key
        // const keys = Object.keys(obj)
        // 此时,迭代出来的是所有的value
        // const values = Object.values(obj)
        // 此时,迭代出来的是我的各个朋友
        // const friends = this.friends
        // 此时,迭代出来的是键值对数组
        const keysValues = Object.entries(obj)
        
        return {
            next: function() {
                if (index < keysValues.length) {
                    return { done: false, value: keysValues[index++] }
                } else {
                    return { done: true }
                }
            }
        }
    }
}    

// 此时,由于obj对象已经实现了可迭代协议,所以它是可以使用for...of的
for (let item of obj) {
    console.log(item)
}

1.3. 原生迭代器对象

  • 事实上,平时使用的许多原生对象都已经实现了可迭代协议,因此它们可以使用for...of
  • 例如:Array/String/Set/Map/NodeList(节点列表)/arguments对象

1.4. 可迭代对象的应用

  • 只有可迭代对象才能使用的一些语法

    • for...of

    • 展开运算符

    • yield*

    • 解构赋值

  • 创建一些对象时,要求必须传入的是可迭代对象

    • new Map(iterable)

    • new WeakMap(iterable)

    • new Set(iterable)

    • new WeakSet(iterable)

  • 使用一些方法时,也要求必须传入的是可迭代对象

    • Array.from(iterbale)

    • Promise.all(iterable)

    • Promise.race(iterable)

1.5. 自定义类的迭代

  • 如果希望自己创建的一个类中的对象,默认也是可以迭代的,那么就可以在创建类的时候,给它加一个实例方法[Symbol.iterator],那么这个类的实例就也可以被for...of遍历了

  • 创建一个classroom类,教室有名字,地址,学生,这个教室可以进来新的学生。并且这个类是可迭代的

class Classroom {
    constructor(name, address, ...students) {
        this.name = name
        this.address = address
        this.students = students
    }

    newStudent(student) {
        this.students.push(student)
    }

    // 在类中写好实例方法,那么这个类所new出来的实例对象,就都是可迭代的了
    [Symbol.iterator]() {
        let index = 0
        let allStudents = this.students

        return {
            next: function() {
                if (index < allStudents.length) {
                    return { done: false, value: allStudents[index++] }
                } else {
                    return { done: true }
                }
            }
        }
    }
}


const Classroom1 = new Classroom("2201", "2号楼", "kobe", "james")
Classroom1.newStudent("coderWhy")
Classroom1.newStudent("linda")

const Classroom2 = new Classroom("2202", "2号楼", "李明", "王红")
Classroom2.newStudent("李强")
Classroom2.newStudent("王虎")

for(let student of Classroom1) {
    console.log(student)
}

for(let student of Classroom2) {
    console.log(student)
}

1.6. 监听迭代器的中断

  • 迭代器有时候会因为某些原因,没有完全迭代,就终止。这个时候就可以使用return来进行监听

  • 比如break或者throw就会中断迭代

class Classroom {
    constructor(name, address, ...students) {
        this.name = name
        this.address = address
        this.students = students
    }

    [Symbol.iterator]() {
        let index = 0
        let allStudents = this.students

        return {
            next: function() {
                if (index < allStudents.length) {
                    return { done: false, value: allStudents[index++] }
                } else {
                    return { done: true }
                }
            },
            // 监听迭代中断
            return: function() {
                console.log("迭代中断了")
                return { done: true }
            }
        }
    }
}


const Classroom1 = new Classroom("2201", "2号楼", "kobe", "Judy")

for(let student of Classroom1) {
    console.log(student)

    if (student === "Judy") {
        // break
        throw new Error("Judy来咯")
    }
}