语法错误:意外标识符(ES6中的生成器)

17

在阅读了MDN上关于生成器的文档后,我想出了这个简单的实验:

var nodes = {
    type: 'root',
    value: [
        { type: 'char', value: 'a' },
        { type: 'char', value: 'b' },
        { type: 'char', value: 'c' },
    ],
};

function* recursiveGenerator(node) {
    if (node.type === 'root') {
        node.value.forEach(function (subnode) {
            for (var suffix of recursiveGenerator(subnode)) {
                yield suffix;
            }
        });
    }

    else {
        yield node.value;
    }
}

for (generated of recursiveGenerator(nodes)) {
    console.log(generated);
}

在使用--harmony标志的node.js v0.11.9上运行它会产生以下错误:

alix@900X4C:~$ node --version
v0.11.9
alix@900X4C:~$ node --harmony test.js 

/home/alix/test.js:14
                yield suffix;
                      ^^^^^^
SyntaxError: Unexpected identifier

我还尝试使用for ... in ...let关键字代替var,但没有任何成功。

我不明白yield*确切的作用是什么, 但如果我在for循环中使用它,我得到的结果是:

alix@900X4C:~$ node --harmony test.js 

/home/alix/test.js:14
                yield* suffix;
                ^
ReferenceError: yield is not defined

如果我用console.log()替换for循环中的yield,则会输出abc。我做错了什么?

编辑

这是一个极简的生成器,展示了node.js知道如何处理生成器:

function* alpha() {
    yield 'a';
    yield 'b';
    yield 'c';
}

for (var suffix of alpha()) {
    console.log(suffix);
}

输出:

alix@900X4C:~$ node --harmony y.js 
a
b
c

解决方案(感谢@Andrew)

function* recursiveGenerator(node) {
    if (node.type === 'root') {
        for (var i = 0; i < node.value.length; ++i) {
            var subnode = node.value[i];

            for (var suffix of recursiveGenerator(subnode)) {
                yield suffix;
            }
        }
    }

    else {
        yield node.value;
    }
}

for (generated of recursiveGenerator(nodes)) {
    console.log(generated);
}

1
我对这里随机出现的 * 有点困惑。这些实际上在你的代码中吗?它们似乎不是你链接的规范的一部分。 - Chris Hayes
1
明白了,谢谢。在 yield* 的情况下,从错误信息来看,我认为 Node.js 期望 yield 是一个变量名。在这两种情况下,它似乎都没有被识别为关键字。 - Chris Hayes
@ChrisHayesпјҡжҳҜзҡ„пјҢжҲ‘д№ҹдјҡиҝҷж ·жғіпјҢдҪҶжҲ‘еҲҡеҲҡж·»еҠ зҡ„зӨәдҫӢиЎЁжҳҺз”ҹжҲҗеҷЁжҳҫ然еҸҜд»ҘдҪҝз”Ё--harmonyж Үеҝ—гҖӮй—®йўҳжҳҜпјҢдёәд»Җд№ҲеҸӘжңүвҖңз®ҖеҚ•вҖқзҡ„з”ҹжҲҗеҷЁе‘ўпјҹ - Alix Axel
2
我对生成器还不够熟悉,无法将其发布为答案,但我认为你的问题在于 forEach()。它会创建自己的作用域(就像 setTimeout() 一样)。如果你将其更改为 for (var i = 0; i < node.value.length; i++),你应该能看到它正常工作。 - Andrew
1
@Andrew 很好的发现。在我看来值得回答。有趣的是,Node 在这里没有输出更好的错误信息,但我猜这对于 yield 来说还为时过早。 - Chris Hayes
显示剩余3条评论
2个回答

30

总结评论:你不能在一个普通的函数内使用yield,因此你不能在forEach中使用yield。下面是一个“生成器化”的forEach示例:

function * foreach (arr, fn) {
  var i

  for (i = 0; i < arr.length; i++) {
    yield * fn(arr[i])
  }
}

function * gen (number) {
  yield number + 1
  yield number + 2
  yield number + 3
}

function * other () {
  yield * foreach([1, 2, 3], gen)
}

for (var i of other()) {
    console.log(i)
}

更新 同时,可以使用这样的辅助函数优雅地解决原始问题:

var nodes = {
  type: 'root',
  value: [
    { type: 'char', value: 'a' },
    { type: 'char', value: 'b' },
    { type: 'root', value: [
        { type: 'char', value: 'c' },
        { type: 'char', value: 'd' },
        { type: 'char', value: 'e' },
      ] 
    },
  ],
}

function * foreach (arr, fn) {
  var i

  for (i = 0; i < arr.length; i++) {
    yield * fn(arr[i])
  }
}

function * value (val) {
  yield val
}

function * recursiveGenerator(node) {
  yield * node.type === 'root' ?  foreach(node.value, recursiveGenerator) : value(node.value)
}

for (var generated of recursiveGenerator(nodes)) {
  console.log(generated);
}

因此,生成器本身变成了一行代码!


1
你不能在函数内部使用 yield,这是令人误解的。只有在生成器函数内部才能使用 yield。但对于示例代码给出肯定评价。 - Chris Hayes
@ChrisHayes:会修复的。=)感谢您提供的示例,yield*确实有助于理解它的作用。 - Alix Axel
@ChrisHayes 有什么误导的地方吗?生成器函数是唯一可以使用yield的地方 => 您可以在函数内部使用yield。或者您是指最好说像“您不能在非生成器函数中使用yield”这样的话? - vkurchatkin
@vkurchatkin 是的,那就是我想说的。抱歉,我的评论有点让人困惑。 - Chris Hayes

3
你已经找到了解决方案,但是为了记录下来,这里提供另一个示例,稍有不同,可以打印出树中所有节点的类型(我增加了一些深度和变量)。
var nodes = {
    type: 'root',
    value: [
        { type: 'char', value: 'a' },
        { type: 'char', value: 'b' },
        { type: 'char', value: [{type: 'int', value: 'c'}] },
    ],
};

var flattenTree = function* (root) {
    yield root.type;
    var subvalues = root.value;
    for(var i in subvalues) {
        var gen = flattenTree(subvalues[i]);
        val = gen.next();
        while(!val.done) {
            if(val.value != undefined)
                yield val.value;
            val = gen.next();
        }
    }
}

var printTree = function() {
    console.log("begin tree");
    var generator = flattenTree(nodes);
    var next = generator.next();
    while(!next.done) {
        console.log(next);
        next = generator.next();
    }
    console.log("finish tree");
}

printTree();

输出:

~/workspace/tmp$ ../node/node --harmony test-gen.js 
begin tree
{ value: 'root', done: false }
{ value: 'char', done: false }
{ value: 'char', done: false }
{ value: 'char', done: false }
{ value: 'int', done: false }
finish tree

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接