如何使用ES6 Proxy实现生成器函数的链式调用?
Got it, let's break this down—using Proxy to add chainable array-like methods to generators is such a cool learning project. Here's a working solution that keeps the generator's lazy evaluation while letting you chain map, filter, and forEach just like you want:
Step 1: The Core Proxy Wrapper
First, we'll create a proxy that tracks your chainable operations and only executes them when you trigger a terminal method like forEach.
function generatorProxy(generatorOrFunc, ...args) { // If we get a generator function, run it with provided args to get an iterator const generator = typeof generatorOrFunc === 'function' ? generatorOrFunc(...args) : generatorOrFunc; // Store the sequence of map/filter operations to apply later const operations = []; const proxy = new Proxy({}, { get(target, prop) { // Terminal method: Execute all operations and iterate over results if (prop === 'forEach') { return (callback) => { for (const value of generator) { let current = value; let shouldKeep = true; // Apply each stored operation in order for (const op of operations) { if (op.type === 'map') { current = op.fn(current); } else if (op.type === 'filter') { if (!op.fn(current)) { shouldKeep = false; break; } } } if (shouldKeep) { callback(current); } } }; } // Chainable method: Add map operation to the queue if (prop === 'map') { return (fn) => { operations.push({ type: 'map', fn }); return proxy; // Return the proxy to keep chaining }; } // Chainable method: Add filter operation to the queue if (prop === 'filter') { return (fn) => { operations.push({ type: 'filter', fn }); return proxy; }; } // Fallback for any other properties return Reflect.get(target, prop); } }); return proxy; }
Step 2: Test with a Range Generator
Let's define a simple range generator to test our proxy:
function* range(start, end) { for (let i = start; i < end; i++) { yield i; } }
Step 3: Use the Chainable Generator
Now you can use it exactly like you described:
// Chain map, filter, and forEach generatorProxy(range, 1, 10) .map(num => num * 2) .filter(num => num > 10) .forEach(num => console.log(num)); // Output: 12, 14, 16, 18
How It Works
- Lazy Evaluation: The generator doesn't start producing values until you call
forEach—allmapandfilteroperations are just stored in a queue until then. - Proxy Interception: The Proxy catches access to
map,filter, andforEach. For chainable methods, it adds the operation to the queue and returns the same proxy to keep the chain going. ForforEach, it iterates through the generator, applies all stored operations, and runs your callback on the final values. - No Prototype Modifications: We're not touching
Generator.prototypeat all—all logic is encapsulated in the proxy wrapper.
Bonus: Extend to Other Methods
You can easily add more chainable or terminal methods, like reduce:
// Inside the Proxy's get handler: if (prop === 'reduce') { return (fn, initialValue) => { let accumulator = initialValue; for (const value of generator) { let current = value; for (const op of operations) { if (op.type === 'map') current = op.fn(current); else if (op.type === 'filter' && !op.fn(current)) { current = null; break; } } if (current !== null) { accumulator = accumulator === undefined ? current : fn(accumulator, current); } } return accumulator; }; } // Usage: const sum = generatorProxy(range, 1, 5) .map(num => num * 3) .reduce((acc, num) => acc + num); // sum = 3+6+9+12 = 30
内容的提问来源于stack exchange,提问作者shu
相关产品推荐
相关产品推荐

