如何使用生成器并能够链接它们的函数扩展数组?

时间:2019-05-24 12:03:33

标签: javascript generator chain

如何使用返回生成器对象并能够链接它们的函数扩展Array?

我尝试扩展Array,以查看是否可以链接生成器函数。第一次调用有效,但下一个调用无效,因为它返回一个生成器对象,我不知道如何扩展它。

Array.prototype.select = function* (fn) {
    let it = this[Symbol.iterator]();
    for (let item of it) yield fn(item);
}

Array.prototype.where = function* (fn) {
    let it = this[Symbol.iterator]();
    for (let item of it) {
        if (fn(item)) yield item;
    }
}

我希望能够将这样的生成器链接到一个数组

let result = arr.select(v => v * 2)
                .where(v => v % 3 === 0);

for (let item of result) console.log(item);

3 个答案:

答案 0 :(得分:2)

您可以将这两种方法都存储为Object的原型函数,并移交迭代器对象,因为这两个函数都使用生成器并返回可迭代的对象。

Object.prototype.select = function* (fn) {
    for (let v of this) yield fn(v);
}

Object.prototype.where = function* (fn) {
    for (let v of this[Symbol.iterator]()) if (fn(v)) yield v;
}

var array = [1, 2, 3, 4, 5, 6, 7, 8, 9],
    result = array[Symbol.iterator]()
        .select(v => v * 2)
        .where(v => v % 3 === 0);

console.log(...result);

答案 1 :(得分:1)

在不修改Object的情况下,您仍然可以使用我创造的技术"superclassing"使这些方法可链接。

首先定义要Array扩展的基类,然后修改Array的原型链以人为地扩展基类。

请注意select()where()方法如何封装原始的生成器函数以返回该类的新实例,以便这些方法可链接。

class Enumerable {
  constructor (getIterator) {
    this[Symbol.iterator] = getIterator;
  }

  select (fn) {
    return new Enumerable(function * () {
      for (const value of this) {
        yield fn(value);
      }
    }.bind(this));
  }

  where (fn) {
    return new Enumerable(function * () {
      for (const value of this) {
        if (fn(value)) yield value;
      }
    }.bind(this));
  }
}

Object.setPrototypeOf(Array, Enumerable);
Object.setPrototypeOf(Array.prototype, Enumerable.prototype);

const array = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const result = array
  .select(v => v * 2)
  .where(v => v % 3 === 0);

for (const item of result) {
  console.log(item);
}

答案 2 :(得分:0)

如果您不介意过度扩展,则第一个迭代器仅返回一个对象。 您可以通过执行console.log(typeof arr.select(v => v * 2));进行检查。

因此,您只需定义:Object.prototype.where = function* (fn) {};

Array.prototype.select = function* (fn) {
    let it = this[Symbol.iterator]();
    for (let item of it) {
      yield fn(item);
    }
};

Object.prototype.where = function* (fn) {
    let it = this[Symbol.iterator]();
    for (let item of it) {
        if (fn(item)) yield item;
    }
};


const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
let result = arr.select(v => v * 2)
                .where(v => v % 3 === 0);

for (let item of result) {
  console.log(item);
}

如果您希望顺序无关紧要,则可以像这样扩展Array和Object。

Array.prototype.select = function* (fn) {
        let it = this[Symbol.iterator]();
        for (let item of it) {
          yield fn(item);
        }
    };

Array.prototype.where = function* (fn) {
    let it = this[Symbol.iterator]();
    for (let item of it) {
        if (fn(item)) yield item;
    }
};

Object.prototype.select = Array.prototype.select;
Object.prototype.where = Array.prototype.where;


const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];

// Chain 1.
let result1 = arr.select(v => v * 2).where(v => v % 3 === 0);

console.log('Chain 1');
for (const item of result1) {
  console.log(item);
}

// Chain 2.
let result2 = arr.where(v => v % 3 === 0).select(v => v * 2);

console.log('Chain 2')
for (const item of result2) {
  console.log(item);
}