Как расширить массив функциями, которые являются генераторами и способны их связывать? - PullRequest
3 голосов
/ 24 мая 2019

Как расширить 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 ]

2 голосов
/ 24 мая 2019

Вы можете сохранить оба метода как функцию-прототип 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 голос
/ 24 мая 2019

Не изменяя Object, вы все равно можете сделать эти методы цепными, используя технику, которую я придумал как "суперклассинг" .

Вы начинаете с определения базового класса, который будет расширяться 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);
}
0 голосов
/ 24 мая 2019

Если вы не возражаете против перерасширения, первый итератор возвращает только объект.Вы можете проверить, выполнив 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);
}
...