Генераторы функций в циклах for - PullRequest
0 голосов
/ 29 декабря 2018

Я пытаюсь лучше понять, как работают генераторы в javascript.

От MDN:

Объявление функции * (ключевое слово функции, сопровождаемое звездочкой) определяетфункция генератора, которая возвращает объект Generator.

function *range(from, to) {
    var counter = from;
    while(to >= counter) {
        yield counter
        counter++
    }        
}

for (var r of range(5, 10)) {
    console.log( r );
}

// print: 5, 6, 7, 8, 9, 10

Я не уверен, насколько точно понимаю, что происходит в приведенном выше фрагменте кода.

Генераторы не должны вызываться, храниться как объекты (генераторы) и затем вызываться методом next().(как показано ниже)

function *foo () {
      yield 'woo';
 }
 var G = foo();
 console.log( G.next() );

В приведенном выше коде в строке 4 с var G = foo(); Я не , вызывающим функцию и создающим новый контекст выполнения, это должно просто вернуть объект генератора(и храните его под этикеткой G).

Я вызову реальную функцию foo, когда я вызову для нее метод next(), в строке 5. В этот момент я создаю контекст выполнения, выполняя код внутри foo и выдает строку "woo".

Как именно должен работать первый фрагмент?

Ответы [ 2 ]

0 голосов
/ 29 декабря 2018

Итак, короткий ответ на ваш вопрос заключается в том, что for ... цикла фактически ожидает только объект, который соответствует Итерируемый протокол .Это объект, функция которого связана с символическим ключом Symbol.iterator, который возвращает итеративный объект.

Генераторы на самом деле обладают этим свойством и функцией next (которая требуется для реализации протокола итератора ).Вы можете увидеть это в следующем фрагменте.

const f = function*() {
  let i = -1; 
  while(true){
        i = i + 1; 
        yield i;
  }
};

const generator = f();
console.log(generator[Symbol.iterator]);
console.log(generator.next);

Так вот почему вам не нужно ни создавать экземпляр объекта-генератора для цикла for...of, ни вызывать next явным образом.Это может быть выполнено автоматически благодаря контракту протокола Iterable.

При этом можно повторить поведение цикла for...of с циклом while, создав объект итератора из объекта генератора.(или позвонив next напрямую) следующим образом:

const f = function*() {
  let i = -1; 
  while(true){
        i = i + 1; 
        yield i;
  }
};

const generator = f();
const iterator = generator[Symbol.iterator]();
    
let j = 0;
let next;
while(j < 5) {
      next = iterator.next();
      console.log('next: ', next);
      j = next.value;
}

В принципе, это, вероятно, также то, что делает нативный код для цикла for...of.

0 голосов
/ 29 декабря 2018

Вызов функции генератора вернет итератор (объект с функцией .next), а цикл for..of будет автоматически перебирать итерируемые объекты.Хотя вы можете заранее сохранить итератор в переменной:

const iter = range(5, 10);
for (var r of iter) {
  ...
}

, в этом нет необходимости - в конце концов, for..of нужна только одна ссылка на итератор.

Вы можете имитировать это в коде, передав одну ссылку на итератор в функцию, которая вызывает каждую функцию .next, пока итератор не будет исчерпан:

function *range(from, to) {
    var counter = from;
    while(to >= counter) {
        yield counter
        counter++
    }        
}

iterate(range(5, 10), num => {
  console.log(num);
});

function iterate(iterator, callback) {
  while (true) {
    const { value, done } = iterator.next();
    if (done) return;
    callback(value);
  }
}

Как видите, нет необходимости сохранять итератор в переменной перед передачей ее в iterate, так же как вы можете использовать вызов range(5, 10) непосредственно дляfor..of цикл, потому что внутренности цикла (или функции) выполняют всю итерацию за вас.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...