2014-12-10 44 views
4

我試圖構建一個可鏈接的JavaScript API。 (我在V8的最新版本中啓用了迭代器和生成器。)在下面的例子中,setState是可鏈接的。它也允許你調用它,而不必明確地創建一個新的實例Builderchain()幫助函數處理該函數並自動返回該實例,因此setState不必擔心它。 (贏得一流功能!)ES6生成器的Function.prototype.apply的等價物

除了可鏈式方法,我想要一些「終止」方法。踢球者是這些「終結者」是發電機。發生器產生一個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" 
] 

回答

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; 
3

的問題是,我無法弄清楚一個相當於f.apply(即,參數)的發電機。

ES6 draft來判斷,你不需要等價的 - 你可以直接使用表達式。生成器函數(構造生成器實例)是函數,它們繼承(通過Generator.prototype)從Function.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 
    } 
} 
+0

是的,你是正確的,例如: (功能*(){})的instanceof功能; // true var gen = function *(){ yield this.random(); } gen.apply(Math,gen).next()。value; 我必須在我的代碼中做別的錯誤。讓我再試一次。 – 2014-12-10 23:33:53

+0

請注意,我的委託函數不再返回一個生成器函數* - 它返回一個正常的函數(返回一個生成器)。 – Bergi 2014-12-11 00:22:15

+0

對。我在下面調整了我的解決方案,返回一個生成器,生成應用生成器的結果。 – 2014-12-11 00:25:47