【问题标题】:Equivalent of Function.prototype.apply for an ES6 generator等效于 ES6 生成器的 Function.prototype.apply
【发布时间】:2015-02-09 06:04:04
【问题描述】:

我正在尝试构建一个可链接的 JavaScript API。 (我在启用了迭代器和生成器的最新版本的 V8 中运行它。)在下面的示例中,setState 是可链接的。它还允许您调用它而无需显式创建新的Builder 实例。 chain() 帮助函数处理并自动返回该实例,因此setState 不必担心它。 (一流的功能!)

除了可链接的方法之外,我还想要一些“终止”方法。更重要的是这些“终结者”是生成器。生成器产生Builder 实例的内部状态。问题是我无法找出与 f.apply(that, arguments) 等效的生成器。我希望能够在运行时调用生成器并设置其this 上下文,就像您可以使用Function.prototype.applyFunction.prototype.call 一样。

Yuck! 评论的解决方法是在Builder.prototype 上公开委托的原始生成器,从委托中调用原始生成器。有什么方法可以实现类似chain 方法的等效包装,而不必在Builder.prototype 上公开_generate 方法?

function Builder() { this.initialState = 'initialState'; };
Builder.prototype.setState = chain(function(k, v) { this[k] = v; });
Builder.prototype.generate = delegate(generate, '_generate'); // Yuck!
Builder.prototype._generate = generate;


function chain(f) {
  return function() {
    var that = (this instanceof Builder) ? this : new Builder();
    f.apply(that, arguments); // Pass through arguments
    return that;
  }
}

function delegate(gen, _gen) {
  return function*() {
    var that = (this instanceof Builder) ? this : new Builder();
    that.setState('delegated', true);               
    yield *that[_gen](); // Yuck!
  }
}

function *generate(opts) {
  var i = 0;
  for(var i = 0; i < 10; i++) {
    yield [Object.keys(this), opts, i].join(',');
  }
}

// Set up a namespace
var ns = {};
ns.setState = Builder.prototype.setState;
ns.generate = Builder.prototype.generate;


var itr = ns
//  .setState('a', 'A')
//  .setState('b', 'B')
//  .setState('c', 'C')
  .generate('options');


var out = [];
for(var value of itr) { out.push(value); }
out;

返回

[
  "initialState,delegated,,0",
  "initialState,delegated,,1",
  "initialState,delegated,,2",
  "initialState,delegated,,3",
  "initialState,delegated,,4",
  "initialState,delegated,,5",
  "initialState,delegated,,6",
  "initialState,delegated,,7",
  "initialState,delegated,,8",
  "initialState,delegated,,9"
]

【问题讨论】:

    标签: javascript generator v8 ecmascript-6


    【解决方案1】:

    问题是我无法为生成器找出与 f.apply(that, arguments) 等效的方法。

    ES6 draft 来看,您不需要等价的 - 您可以按原样使用表达式。生成器函数(构造生成器实例)是函数,它们从Function.prototype 继承(通过Generator.prototype);您可以在它们上使用.call.apply,就像在任何其他功能上一样。

    所以以下应该可以工作:

    function delegate(gen) {
      return function() {
        var that = (this instanceof Builder) ? this : new Builder();
        that.setState('delegated', true);               
        return gen.apply(that, arguments); // Pass through arguments
      }
    }
    

    【讨论】:

    • 是的,你是对的,例如:(function* () {}) instanceof Function; // true var gen = function* () { yield this.random(); } gen.apply(Math, gen).next().value;我一定在我的代码中做错了什么。让我再刺一次。
    • 请注意,我的 delegate 函数不再返回生成器 function* - 它返回正常的 function(返回生成器)。
    • 对。我在下面调整了我的解决方案以返回一个生成器,该生成器产生应用生成器的结果。
    • 不必要的复杂 :-) 此外,它不再使用普通函数作为参数 gendelegate...
    • 是的,function 与生成器 function* 几乎没有区别,只要它们在被调用时都返回一个可迭代的生成器。
    【解决方案2】:

    密钥是匿名生成器包装器中的yield *gen.apply(that, arguments)

    function Builder() { this.initialState = 'initialState'; };
    Builder.prototype.setState = chain(function(k, v) { this[k] = v; });
    Builder.prototype.generate = delegate(generate); 
    
    // Reuses or creates a Builder instance and makes it `this` for calling `f`.
    // Returns the Builder instance.
    function chain(f) {
      return function() {
        var that = (this instanceof Builder) ? this : new Builder();
        f.apply(that, arguments); // Pass through arguments
        return that;
      }
    }
    
    // Similar to `chain()` to create a Builder instance if it doesn't exist.
    // Generators are terminal, though, so this returns the FunctionGenerator.
    function delegate(gen) {
      return function*() {
        var that = (this instanceof Builder) ? this : new Builder();
        that.setState('delegated', true);               
        yield *gen.apply(that, arguments);
      }
    }
    
    function *generate(opts) {
      var i = 0;
      for(var i = 0; i < 10; i++) {
        yield [Object.keys(this), opts, i].join(',');
      }
    }
    
    // Set up a namespace
    var ns = {};
    ns.setState = Builder.prototype.setState;
    ns.generate = Builder.prototype.generate;
    
    
    var itr = ns
    //  .setState('a', 'A')
    //  .setState('b', 'B')
    //  .setState('c', 'C')
      .generate('options');
    
    
    var out = [];
    for(var value of itr) { out.push(value); }
    out;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-01-10
      • 2021-03-18
      • 2018-08-29
      • 2019-05-03
      • 1970-01-01
      • 2018-04-14
      • 1970-01-01
      相关资源
      最近更新 更多