在生成器函数和它们的迭代器下面重新布线,iterable在哪里?



深入了解GeneratorFunction和Generators。我想给IteratorResult添加有用的属性也可能是给IteratorResult。值本身。这都是统治世界的宏伟计划的一部分。但是我想知道如何获得Generator函数提供的实际迭代器?

首先我注意到生成器函数本身在执行时没有this值(它是全局对象),无论它是先被调用还是在next()之后继续:

function* range(start = 0, end = Infinity, step = 1) {      
console.debug("awon", this);
for(let i = start; Math.sign(step)*(end - i) > 0; i += step) {
console.debug("tawoo", this);
i = (yield i)||i; // capricious trickery allowing us to go back or skip with next(arg)
console.debug("esree", this);
}
}

现在我让自己成为一个有用的侧门,可以对生成器执行各种神奇的操作(如定义函数map, reduce, filter等):

const Generator_prototype = Object.getPrototypeOf(range.prototype);

,这里我想捕获Iterator对象本身的提供:

const originalIterate = Generator_prototype[Symbol.iterator];
function iterate() {
console.log("iter", this);
const iterator = originalIterate.apply(this);
iterator.parent = this;
return iterator;
}
Generator_prototype[Symbol.iterator] = iterate;

因为,就像我说的,我想要按摩来自next()的IterationResult:

const originalNext = Generator_prototype.next;
function next(...args) {
console.log("next", this);
let value = originalNext.apply(this, args);
if(this.decorate)
value = this.decorate(value); 
return this.current = value; 
}
Generator_prototype.next = next;

和这个:

[...range(1,3)]

给出输出:

iter range {<suspended>}
next range {<suspended>, parent: range}
next range {<suspended>, parent: range, current: {…}}
next range {<suspended>, parent: range, current: {…}}
> (2) [1, 2]

所以,我没有得到Iterable,只有Iterator。函数[符号。]在某个对象上调用iterator],但该对象以某种方式将thisArg绑定到它应该返回的迭代器上。值得注意的是:

iterator = range(1,3)[Symbol.iterator]()
iterator.parent == iterator // > true

这怎么可能呢?我正在用我自己的方法替换@@iterator方法,当它被调用时它已经有了一个迭代器作为thisArg,这和我通过对this应用originalIterator来请求标准迭代器时得到的迭代器是完全一样的?我头晕目眩!

如何获得iterable,而不是iterator

?

主要有两点:

  1. 不是所有的迭代器都有一个可迭代对象(毕竟,它只是一个直接或间接实现Symbol.iterator方法的对象)。

  2. 所有内置迭代器都使用一个原型(遗憾的是,它没有附加公共名称),通过实现Symbol.iterator方法使它们可迭代,让它只返回this。这可能看起来很奇怪,但这就是为什么我们可以在内置迭代器上使用for-of(因为它们也是可迭代的)。这种内置的行为就是为什么你在问题末尾的代码中得到iterator.parent === iteratortrue

    注意,这只适用于内置迭代器。人们可以在不继承标准原型的情况下创建自己的迭代器(毕竟,继承标准原型是一件很痛苦的事情),有时确实可以这样做。

range函数或它返回的生成器对象中没有涉及可迭代对象(除了生成器对象本身,因为上面的#2)。

您的主要目标似乎是增强或修补内置生成器。你可以通过它的原型来做,但是我不会. 以下示例将range生成器返回的值加倍:

// Get the prototype used by built-in generators and its `next` method
const genProto = Object.getPrototypeOf(Object.getPrototypeOf((function* () {})()));
const defaultGenNext = genProto.next;
// Replace it with our own `next`
genProto.next = function next(...args) {
console.log(`Args:   ${JSON.stringify(args)}`);
const result = defaultGenNext.apply(this, args);
if (typeof result.value === "number") {
console.log("Doubling");
result.value *= 2;
}
console.log(`Result: ${JSON.stringify(result)}`);
return result;
};

生活的例子:

// Get the prototype used by built-in generators and its `next` method
const genProto = Object.getPrototypeOf(Object.getPrototypeOf((function* () {})()));
const defaultGenNext = genProto.next;
// Replace it with our own `next`
genProto.next = function next(...args) {
console.log(`Args:   ${JSON.stringify(args)}`);
const result = defaultGenNext.apply(this, args);
if (typeof result.value === "number") {
console.log("Doubling");
result.value *= 2;
}
console.log(`Result: ${JSON.stringify(result)}`);
return result;
};
// Your `range` function
function* range(start = 0, end = Infinity, step = 1) {
// console.debug("awon", this);
for (let i = start; Math.sign(step) * (end - i) > 0; i += step) {
// console.debug("tawoo", this);
i = (yield i) || i; // capricious trickery allowing us to go back or skip with next(arg)
// console.debug("esree", this);
}
}
// Example
for (const value of range(1, 5)) {
console.log(value);
}
.as-console-wrapper {
max-height: 100% !important;
}

或者如果我们想要的话,用推回生成器:

// Get the prototype used by built-in generators and its `next` method
const genProto = Object.getPrototypeOf(Object.getPrototypeOf((function* () {})()));
const defaultGenNext = genProto.next;
// Replace it with our own `next`
genProto.next = function next(...args) {
console.log(`Args:   ${JSON.stringify(args)}`);
const result = defaultGenNext.apply(this, args);
if (typeof result.value === "number") {
console.log("Doubling");
result.value *= 2;
}
console.log(`Result: ${JSON.stringify(result)}`);
return result;
};
// Your `range` function
function* range(start = 0, end = Infinity, step = 1) {
// console.debug("awon", this);
for (let i = start; Math.sign(step) * (end - i) > 0; i += step) {
// console.debug("tawoo", this);
i = (yield i) || i; // capricious trickery allowing us to go back or skip with next(arg)
// console.debug("esree", this);
}
}
// Example
let flag = true;
let result;
let it = range(1, 5);
while (!result?.done) {
if (result) {
console.log(result.value);
}
let param;
if (flag && result && result.value === 4) {
param = 1;
flag = false;
}
result = it.next(param);
}
.as-console-wrapper {
max-height: 100% !important;
}

double显然只是一个示例,请注意,您可以访问传递给next的值以及它返回的结果对象。

再说一次,我不会那样做,至少有两个原因:

  1. 乱搞内置原型通常不会有好结果。
  2. 不是所有的生成器都继承自生成器原型。

毕竟,生成(像迭代一样)只是一个协议。这意味着你可以用自己的代码实现它,完全绕过任何内置的东西。

我要做的是写一个函数来封装你想要转换的生成器。例如,这里有一个简单的双倍器,它不支持回推:

function* double(target) {
for (const value of target) {
yield value * 2;
}
}

那就用double(range(1, 5))或者别的什么代替range(1, 5)

生活的例子:

// Your `range` function
function* range(start = 0, end = Infinity, step = 1) {
// console.debug("awon", this);
for (let i = start; Math.sign(step) * (end - i) > 0; i += step) {
// console.debug("tawoo", this);
i = (yield i) || i; // capricious trickery allowing us to go back or skip with next(arg)
// console.debug("esree", this);
}
}
function* double(target) {
for (const value of target) {
yield value * 2;
}
}
// Example
for (const value of double(range(1, 5))) {
console.log(value);
}
.as-console-wrapper {
max-height: 100% !important;
}

或者你可以写它来支持回推:

// Your `range` function
function* range(start = 0, end = Infinity, step = 1) {
// console.debug("awon", this);
for (let i = start; Math.sign(step) * (end - i) > 0; i += step) {
// console.debug("tawoo", this);
i = (yield i) || i; // capricious trickery allowing us to go back or skip with next(arg)
// console.debug("esree", this);
}
}
function* double(target) {
const it = target[Symbol.iterator]();
let lastArg;
let result;
while (!(result = lastArg === undefined ? it.next() : it.next(lastArg)).done) {
lastArg = yield result.value * 2;
}
}
// Example
let flag = true;
let result;
let it = double(range(1, 5));
while (!result?.done) {
if (result) {
console.log(result.value);
}
let param;
if (flag && result && result.value === 4) {
param = 1;
flag = false;
}
result = it.next(param);
}
.as-console-wrapper {
max-height: 100% !important;
}

最新更新