JavaScript生成器函数的for of原理解析及生成器算法拓展

448 阅读2分钟

原理解析

    大多数情况下,for of语法都用在数组和对象上面,可以遍历它们每一项的值,然而for of也可以用在生成器函数上面,因此来探究一下这是怎么实现的。

先写一个简单的生成器函数:

function * gen() {
    yield 1
    yield 2
    yield 3
}
var g = gen()
for(var n of g) {
    console.log(n)
    break
}

    此时会在控制台中输出数字1,因为加了break,那么此时的生成器函数有没有执行结束呢?我们再次调用一下g.next()看看。

g.next()
{value: undefined, done: true}

    发现此时生成器函数已经执行结束了,因为g.next()返回对象的done属性已经为true,而且value值也为undefined,说明函数也返回了undefined。综上所述,for of操作生成器函数,不管中途有没有break,它是直接将函数执行到最后的。

    接下来手动实现以下这个for of:

形参generator为被调用的生成器函数,在这里对应开头代码块中的变量g;
形参action为一个回调函数,具体要干嘛可以自己定义;
function forof(generator, action) {
    var generated = generator.next()
    while(!generated.done) {
        if(action(generated.value) === false) {
            generator.return()
        }
        generated = generator.next()
    }
}
forof(g, num => {
    console.log(num)
    return false
})

    此时控制台中会输出数字1,再调用一个g.next()看看有没有实现break的效果

g.next()
{value:undefined,done:true}

算法拓展

    这道题是leetcode 173. 二叉搜索树迭代器 ,题目我就不拉过来了,写一下两种解法

1.运用javascript自身的生成器函数来解决(与本文相关的知识点)

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 */
var BSTIterator = function(root) {
    this.generator = traverse(root)
    this.generated = this.generator.next()

    function * traverse(root) {
        if(root) {
            yield * traverse(root.left)
            yield root.val
            yield * traverse(root.right)
        }
    }
};

/**
 * @return the next smallest number
 * @return {number}
 */
BSTIterator.prototype.next = function() {
    var value = this.generated.value
    this.generated = this.generator.next()
    return value
};

/**
 * @return whether we have a next smallest number
 * @return {boolean}
 */
BSTIterator.prototype.hasNext = function() {
    return !this.generated.done
};

2.用题目要求的栈方法来解决

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 */
var BSTIterator = function(root) {
    this.stack = []
    this.result = []
    var p = root
    while(p){
        this.stack.push(p)
        p = p.left
    }
};

/**
 * @return the next smallest number
 * @return {number}
 */
BSTIterator.prototype.next = function() {
    var node = this.stack.pop()
    var p = node.right
    while(p){
        this.stack.push(p)
        p = p.left
    }
    return node.val
};

/**
 * @return whether we have a next smallest number
 * @return {boolean}
 */
BSTIterator.prototype.hasNext = function() {
    return this.stack.length > 0
};

从第二种解法来讲,因为是二叉搜索树,大体上就可以作为二叉树的中序遍历来解决,只不过是被割开了,它是一个个返回的。







----分割线----

Number.prototype[Symbol.iterator] = function * () {
    for(var i = 1; i <= this; i++) {
        yield i
    }
}
for(var i of 10) {
    console.log(i)
}