JavaScript/ES6:如何从类返回多个迭代器?

Jav*_*ser 3 javascript iterator ecmascript-6

我正在实现一个双向链表作为编程练习的一部分,并且我希望允许开发人员使用符号向前和向后迭代其节点for...in

最基本的数据结构如下所示:

class DoublyLinkedList {
    constructor(data) {
        if (data) {
            this.head = new DoublyLinkedListNode(data)
        } else {
            this.head = null
        }
    }

    append = (data) => {
        if (!this.head) {
            this.prepend(data)
        } else {
            const newTail = new DoublyLinkedListNode(data)
            let current = this.head
            while(current.next) {
                current = current.next
            }

            current.next = newTail
            newTail.prev = current
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

接下来我添加了生成器函数:

    *values() {
        let current = this.head
        while (current) {
            yield current.data;
            current = current.next;
        }
    }

    *valuesBackward() {
        let currentForwards = this.head
        while (currentForwards.next) {
            currentForwards = currentForwards.next
        }

        const tail = currentForwards
        let currentBackwards = tail
        while (currentBackwards) {
            yield currentBackwards.data
            currentBackwards = currentBackwards.prev
        }
    }
Run Code Online (Sandbox Code Playgroud)

我可以添加一个向前迭代器,并将以下内容添加到类中:

[Symbol.iterator]() { return this.values()}
Run Code Online (Sandbox Code Playgroud)

我尝试将以下两项添加到班级中:

iterateForward = () => [Symbol.iterator] = () => this.valuesBackward()
iterateBackward = () => [Symbol.iterator] = () => this.valuesBackward()
Run Code Online (Sandbox Code Playgroud)

然后尝试迭代使用,for (node in list.iterateForward())但失败并出现错误TypeError: undefined is not a function

我想看看代码是有意义的,所以接下来我尝试了:

    iterateForward = () => {
        const vals = this.values()

        const it = {
            [Symbol.iterator]() {
                return vals()
            }
        }
        return it
    }
Run Code Online (Sandbox Code Playgroud)

这没有错误,但迭代不起作用 - 迭代器运行了零次。

我在这里缺少什么?有可能实现我想要的吗?

jfr*_*d00 5

这些东西经常让我感到困惑,所以这里有一个我们都可以参考的摘要。

这是背景

Aniterable是一个具有该[Symbol.iterator]属性的对象,当您将该属性作为函数调用时,它会返回一个迭代器对象。

迭代器对象有一个属性.next(),每次调用该函数时,它都会返回具有预期属性的对象{value: x, done: false}。迭代器对象通常将迭代的状态保存在这个单独的对象中(因此迭代器可以彼此独立)。

因此,为了支持多个迭代器,您可以创建多个方法,其中每个方法返回一个不同的可迭代对象,每个方法都有自己的迭代器对象[Symbol.iterator],在调用时返回不同的迭代器对象。

因此,回顾一下,您:

  1. 调用返回可迭代对象的方法
  2. [Symbol.iterator]可迭代对象是一个具有属性并可以访问原始对象数据的对象。
  3. 当您调用该属性中的函数时[Symbol.iterator],您将获得一个迭代器对象。
  4. 迭代器对象包含一个.next()方法,可以获取序列中的每个项目,并且通过{value: x, done: false}每次调用时返回一个这样的对象来实现这一点.next()

您可以跳过第 1 步,只让您的核心对象拥有[Symbol.iterator]该属性。这基本上成为您的默认迭代。如果你这样做:

for (let x of myObj) {
    console.log(x);
}
Run Code Online (Sandbox Code Playgroud)

它将访问myObj[Symbol.iterator]()并获取迭代器。但是,如果您希望有不止一种方法来迭代您的集合,那么您可以创建单独的函数,每个函数返回自己的可迭代对象(它们自己的对象,具有自己的[Symbol.iterator]属性)。


在数组中,您有.entries().values()作为返回不同迭代的两个方法的示例,这两个方法产生不同的迭代器。

let x = ['a', 'b', 'c'];

for (let v of x.values()) {
    console.log(v);                
}
Run Code Online (Sandbox Code Playgroud)

这给出了输出:

'a'
'b'
'c'
Run Code Online (Sandbox Code Playgroud)

或者,对于.entries()

let x = ['a', 'b', 'c'];

for (let v of x.entries()) {
    console.log(v);                
}

[0, "a"]
[1, "b"]
[2, "c"]
Run Code Online (Sandbox Code Playgroud)

因此,每个 和.values()都会.entries()返回一个不同的对象,每个对象都有不同的对象[Symbol.iterator],当作为函数调用时,会为其唯一序列返回不同的迭代器函数。

并且,在数组的情况下,.values()返回一个函数,当调用该函数时,它会为您提供与直接迭代数组完全相同的迭代器(例如[Symbol.iterator]数组本身的属性)。

现在,针对您的具体情况

您想要创建两个方法,假设.forward()每个.backward()方法都创建一个具有属性的对象[Symbol.iterator],该属性是一个函数,在调用时返回其唯一的迭代器对象。

因此,obj.forward()将返回一个具有属性的对象[Symbol.iterator],该属性是一个函数,在调用时返回具有适当的.next()向前迭代属性和适当的起始状态的迭代器对象。

因此,obj.backward()将返回一个具有属性的对象[Symbol.iterator],该属性是一个函数,在调用时返回具有适当属性的迭代器对象.next()以向后迭代和适当的起始状态。

这是使用数组的示例:

for (let x of myObj) {
    console.log(x);
}
Run Code Online (Sandbox Code Playgroud)