【问题标题】:How can I write a generator in a JavaScript class?如何在 JavaScript 类中编写生成器?
【发布时间】:2017-01-04 23:40:44
【问题描述】:

通常,我会这样写代码:

//definition
exports.getReply = function * (msg){
    //...
    return reply; 
}
//usage
var msg = yield getReply ('hello');

但是如何在 es6 类中编写和使用生成器呢?我试过这个:

class Reply{
    *getReply (msg){
        //...
        return reply; 
    }
     *otherFun(){
        this.getReply();  //`this` seem to have no access to `getReply`
    }
}
var Reply = new Reply();
Reply.getReply();   //out of class,how can I get access to `getReply`?

我也试过了:

 class Reply{
      getReply(){
         return function*(msg){
            //...
            return reply;
         }
      }
    }

所有这两种方法似乎都是错误的答案。那么如何在类中正确编写生成器函数呢?

【问题讨论】:

  • “所有这两种方法似乎都是错误的答案”---这是什么意思?您在类中定义生成器函数的方式与“普通”生成器函数相同。
  • @zerkms 你能举个例子吗?当我尝试这样做时,它会产生语法错误。
  • 您的代码(第一个示例中的类定义)在语法上是正确的,并且运行时没有语法错误。
  • 你需要使用不同的变量名,比如var reply = new Reply();
  • @Bergi,这个问题不是引用问题的重复,这个是关于分类的,另一个是关于对象文字的

标签: javascript ecmascript-6


【解决方案1】:

编辑:添加更多示例。
您的class 定义(几乎)是正确的。错误出现在实例化var Reply = new Reply(); 中。这试图重新定义分配给类名的变量。 generator 函数也应该是 yield 的东西。我详细阐述了一些 OP 代码来展示工作示例。

class Reply {
  //added for test purpose
  constructor(...args) {
    this.args = args;
  }
  * getReply(msg) {
      for (let arg in this.args) {
        let reply = msg + this.args[arg];
        //generator should yield something
        yield reply;
      }
      //next call returns (yields) {done:true,value:undefined}
  }
  * otherFun() {
      yield this.getReply('Nice to meet you '); //yields Generator object
      yield this.getReply('See you '); //Yes, this can access 
      //next call yields {done:true, value:undefined}
  }
  * evenMore() {
      yield* this.getReply('I miss you '); //yields generator result(s)
      yield* this.getReply('I miss you even more ');
  }
}
//now test what we have
const reply = new Reply('Peter', 'James', 'John');
//let and var here are interchangeable because of Global scope
var r = reply.getReply('Hello ');
var msg = r.next(); //{done:false,value:"..."}
while (!msg.done) {
  console.log(msg.value);
  msg = r.next();
}
var other = reply.otherFun();
var o = other.next(); //{done:false,value:Generator}
while (!o.done) {
  let gen = o.value;
  msg = gen.next();
  while (!msg.done) {
    console.log(msg.value);
    msg = gen.next();
  }
  o = other.next();
}
var more = reply.evenMore();
msg = more.next();
while (!msg.done) {
  console.log(msg.value);
  msg = more.next();
}
//update of 1/12/2019
//more examples
for (let r of reply.getReply('for ')) {
  console.log(r);
}
for (let r of reply.evenMore()) {
  console.log(r);
}
//note that the following doesn't work because of lack of star (*) inside the generator function
for (let r of reply.otherFun()) {
  console.log(r);
}

2019 年 1 月 12 日更新

正如@BugBuddy for..of 所建议的那样,循环看起来更好(但并非在所有情况下都有效)。查看 sn-p 中的更新行。

【讨论】:

  • Alex,我想知道你为什么使用yield this.getReply('Nice to meet you '); 而不是yield * this.getReply('Nice to meet you')getReply 本身一个生成器函数,不是吗?
  • 你能在你的例子中展示for...of的用法吗?这不会产生比 while 循环更好的代码吗?
  • @BugBuddy 感谢您的建议并查看更新的答案。
  • 如果您有兴趣,Symbol.iterator 是您可以添加到您的出色示例中的另一件事:内部类回复:*[Symbol.iterator]() { for (let a of this.args) {yield a;} } 示例使用:for (let arg of reply) { console.log('I came from Symbol.iterator: ' + arg);}
  • 请注意,这不能静态工作,即static * getReply() {} 无效,对于这种情况(也可以非静态工作)使用getter,即static get getReply() {return *function() {} }
【解决方案2】:

TL;来自 Google 的困惑访问者的 DR:

在 Javascript 中,如何在类中编写生成器函数?

class A {
    * values() {
        yield "a value";
        yield* [1, 2, 3, 4, 5];
    }
}

在语法上是正确的。有用。不客气,现在被解雇了。

【讨论】:

    【解决方案3】:

    生成器是带有.next() 的函数,用于获取yield'ed 值,或者您可以yield 生成器函数让它知道它在遇到yield 语句时不需要“等待”一个要调用的.next (reply.getReply().next(fn))

    您的第二段代码几乎是正确的:

    class Reply{
        *getReply (msg){
            //...
            return reply; 
        }
         *otherFun(){
            this.getReply();  //`this` seem to have no access to `getReply`
        }
    }
    var Reply = new Reply();
    Reply.getReply();   //out of class,how can I get access to `getReply`?
    

    首先,在 ES6 中工作时请使用constlet,并且只对类使用大写变体。 您正试图用var Reply = 语句覆盖class Reply 语句,这是不可能的,因为Identifier 'Reply' 已经声明。

    您正在寻找的答案如下:
    就像您在第一个示例中所做的那样,您应该 yield 生成器函数,所以您的可能应该是这样的:

    class Reply{
        *getReply (msg){
            // yield something here, otherwise you should use a normal function
            return reply;
        }
         *otherFun(){
            const reply = yield this.getReply();  // yield the generator so it does what it needs to and doesn't wait for the .next() to be called on it
            return `${reply} within class ${this.constructor.name}`;
        }
    }
    const reply = new Reply();
    const answer = yield reply.getReply('foo'); 
    // getReply is a generator function, so it needs a `yield` or `.next()` to run beyond the first `yield` in the function
    

    【讨论】:

    • "你应该 yield 生成器函数" - 不。您答案中的代码会生成生成器,您也不应该这样做。你应该yield* generators。
    • “您实际上是在用 var Reply = 语句覆盖类回复语句。” 这是不可能的。 "const reply = yield this.getReply();" 为什么是const reply = "yield reply.getReply('foo');" yield 在生成器函数之外?
    • @Bergi yield* 是什么意思?没有yield*...这样的东西吗?这是function *something(){}yield... @zeroflagL 我更正了覆盖解释,它实际上会返回一个错误,即Identifier 'Reply' 已经存在。 @ 987654345@,因为它是Reply 的新实例,所以你可能不希望它被其他东西覆盖,不,不可能在生成器函数之外调用yield,但这是这样做的方法这。我将更改答案以包含更多关于如何在生成器函数之外获取返回值的代码
    • @CreasolDev 当然有,向上看it
    • @Bergi 好吧,对不起,我不知道,但如果只是他想要的返回值,没有理由使用yield* 使用类似 co 的东西似乎更合乎逻辑.wrap,它将返回一个带有所需值的承诺(在生成器之外)
    猜你喜欢
    • 2017-08-16
    • 2016-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-02
    • 2020-05-01
    相关资源
    最近更新 更多