- Оригинальный адрес:What are JavaScript Generators and how to use them
- Оригинальный автор:Vladislav Stepanov
- Перевод с:Программа перевода самородков
- Постоянная ссылка на эту статью:GitHub.com/rare earth/gold-no…
- Переводчик:lsvih
- Корректор:zhongdeming428,старый профессор
В этой статье мы узнаем о генераторах, представленных в ECMAScript 6. Давайте посмотрим, что это такое на самом деле, а затем проиллюстрируем его использование на нескольких примерах.
Что такое генератор JavaScript?
Генератор — это функция, которую можно использовать для управления итератором, который можно приостановить и возобновить в любое время.
Приведенное выше описание мало что объясняет, давайте рассмотрим несколько примеров, чтобы объяснить, что такое генератор и как генераторы связаны сдля циклаВ чем разница между итераторами типа .
Ниже приведендля цикланапример, он вернет некоторое значение сразу после выполнения. Этот код просто генерирует числа 0-5.
for (let i = 0; i < 5; i += 1) {
console.log(i);
}
// 它将会立刻返回 0 -> 1 -> 2 -> 3 -> 4
Теперь посмотрим на функции генератора.
function * generatorForLoop(num) {
for (let i = 0; i < num; i += 1) {
yield console.log(i);
}
}
const genForLoop = generatorForLoop(5);
genForLoop.next(); // 首先 console.log - 0
genForLoop.next(); // 1
genForLoop.next(); // 2
genForLoop.next(); // 3
genForLoop.next(); // 4
Что это делает? Это на самом деле только для приведенного выше примерадля циклаСделал небольшое изменение, но сделал большую разницу. Это изменение вызвано самой важной особенностью генераторов — они производят следующее значение только тогда, когда это необходимо, а не все сразу. В некоторых ситуациях эта функция очень удобна.
синтаксис генератора
Как определить функцию генератора? Различные возможные определения перечислены ниже, но самое главное — поставить звездочку после ключевого слова функции.
function * generator () {}
function* generator () {}
function *generator () {}
let generator = function * () {}
let generator = function* () {}
let generator = function *() {}
let generator = *() => {} // SyntaxError
let generator = ()* => {} // SyntaxError
let generator = (*) => {} // SyntaxError
Как показывает приведенный выше пример, мы не можем использовать стрелочные функции для создания генератора.
Генератор создается как метод ниже. Определение метода аналогично определению функции.
class MyClass {
*generator() {}
* generator() {}
}
const obj = {
*generator() {}
* generator() {}
}
yield
Теперь давайте посмотрим на новые ключевые слова.yield
. это чем-то похожеreturn
, но не совсем то же самое.return
просто вернет значение после завершения вызова функции, вreturn
Вы не можете ничего делать после заявления.
function withReturn(a) {
let b = 5;
return a + b;
b = 6; // 不可能重新定义 b 了
return a * b; // 这儿新的值没可能返回了
}
withReturn(6); // 11
withReturn(6); // 11
а такжеyield
работать по-разному.
function * withYield(a) {
let b = 5;
yield a + b;
b = 6; // 在第一次调用后仍可以重新定义变量
yield a * b;
}
const calcSix = withYield(6);
calcSix.next().value; // 11
calcSix.next().value; // 36
использоватьyield
Возвращаемое значение будет возвращено только один раз, и когда вы снова вызовете ту же функцию, она будет выполняться до следующегоyield
Предложение (Примечание переводчика: предыдущееyield
больше ничего не возвращает).
В генераторах мы обычно получаем объект на выходе. Этот объект имеет два свойства:value
а такжеdone
. как ты думаешь,value
для возвращаемого значения,done
покажет, сделал ли генератор свою работу.
function * generator() {
yield 5;
}
const gen = generator();
gen.next(); // {value: 5, done: false}
gen.next(); // {value: undefined, done: true}
gen.next(); // {value: undefined, done: true} - 之后的任何调用都会返回相同的结果
В генераторе можно не только использоватьyield
, вы также можете использоватьreturn
чтобы вернуть тот же объект. Однако после выполнения функции до первогоreturn
заявление, генератор завершит свою работу.
function * generator() {
yield 1;
return 2;
yield 3; // 到不了这个 yield 了
}
const gen = generator();
gen.next(); // {value: 1, done: false}
gen.next(); // {value: 2, done: true}
gen.next(); // {value: undefined, done: true}
итерация делегата yield
звездочкаyield
Его работу можно делегировать другому генератору. Таким образом, вы можете соединить несколько генераторов вместе.
function * anotherGenerator(i) {
yield i + 1;
yield i + 2;
yield i + 3;
}
function * generator(i) {
yield* anotherGenerator(i);
}
var gen = generator(1);
gen.next().value; // 2
gen.next().value; // 3
gen.next().value; // 4
Прежде чем перейти к следующему разделу, давайте понаблюдаем за поведением, которое на первый взгляд может показаться странным.
Ниже приведен обычный код, который не будет сообщать об ошибках, что указывает на то, чтоyield
допустимыйnext()
Возвращает переданное значение после вызова метода:
function * generator(arr) {
for (const i in arr) {
yield i;
yield yield;
yield(yield);
}
}
const gen = generator([0,1]);
gen.next(); // {value: "0", done: false}
gen.next('A'); // {value: undefined, done: false}
gen.next('A'); // {value: "A", done: false}
gen.next('A'); // {value: undefined, done: false}
gen.next('A'); // {value: "A", done: false}
gen.next(); // {value: "1", done: false}
gen.next('B'); // {value: undefined, done: false}
gen.next('B'); // {value: "B", done: false}
gen.next('B'); // {value: undefined, done: false}
gen.next('B'); // {value: "B", done: false}
gen.next(); // {value: undefined, done: true}
В этом примере вы можете увидетьyield
По умолчаниюundefined
, но если мы звонимyield
При передаче любого значения он вернет значение, которое мы передали. Вскоре мы воспользуемся этой возможностью.
инициализация и метод
Генераторы можно использовать повторно, но их нужно инициализировать. К счастью, метод инициализации очень прост.
function * generator(arg = 'Nothing') {
yield arg;
}
const gen0 = generator(); // OK
const gen1 = generator('Hello'); // OK
const gen2 = new generator(); // 不 OK
generator().next(); // 可以运行,但每次都会从头开始运行
Как показано выше,gen0
а такжеgen1
не будут влиять друг на друга,gen2
Я не побегу (дается). Поэтому процесс инициализации для обеспечения статуса программы очень важен.
Давайте взглянем на генераторы методов для нас.
метод следующий()
function * generator() {
yield 1;
yield 2;
yield 3;
}
const gen = generator();
gen.next(); // {value: 1, done: false}
gen.next(); // {value: 2, done: false}
gen.next(); // {value: 3, done: false}
gen.next(); // {value: undefined, done: true} 之后所有的 next 调用都会返回同样的输出
Это самый распространенный метод. Он возвращает следующий объект при каждом вызове. По окончании работы генератораnext()
будуdone
свойство установлено наtrue
,value
свойство установлено наundefined
.
Мы можем не только использоватьnext()
для перебора генератора вы также можете использоватьfor of
Цикл, чтобы получить все значения генератора (не объекта) сразу.
function * generator(arr) {
for (const el in arr)
yield el;
}
const gen = generator([0, 1, 2]);
for (const g of gen) {
console.log(g); // 0 -> 1 -> 2
}
gen.next(); // {value: undefined, done: true}
но это не работаетfor in
цикл, и свойства не могут быть доступны напрямую с числовыми индексами:generator[0] = undefined
.
метод возврата()
function * generator() {
yield 1;
yield 2;
yield 3;
}
const gen = generator();
gen.return(); // {value: undefined, done: true}
gen.return('Heeyyaa'); // {value: "Heeyyaa", done: true}
gen.next(); // {value: undefined, done: true} - 在 return() 之后的所有 next() 调用都会返回相同的输出
return()
Любой код в генераторе будет проигнорирован. Он будет установлен в соответствии с переданным значениемvalue
, и воляdone
установить какtrue
. любой вreturn()
осуществляется послеnext()
звонки вернутсяdone
собственностьtrue
Объект.
метод броска()
function * generator() {
yield 1;
yield 2;
yield 3;
}
const gen = generator();
gen.throw('Something bad'); // 会报错 Error Uncaught Something bad
gen.next(); // {value: undefined, done: true}
throw()
То, что он делает, очень просто — просто выдает ошибку. мы можем использоватьtry-catch
обрабатывать.
Реализация пользовательского метода
Так как у нас нет прямого доступаGenerator
конструктор, поэтому, как добавлять новые методы, нужно объяснять отдельно. Вот мой метод, вы также можете реализовать его по-разному:
function * generator() {
yield 1;
}
generator.prototype.__proto__; // Generator {constructor: GeneratorFunction, next: ƒ, return: ƒ, throw: ƒ, Symbol(Symbol.toStringTag): "Generator"}
// 由于 Generator 不是一个全局变量,因此我们只能这么写:
generator.prototype.__proto__.math = function(e = 0) {
return e * Math.PI;
}
generator.prototype.__proto__; // Generator {math: ƒ, constructor: GeneratorFunction, next: ƒ, return: ƒ, throw: ƒ, …}
const gen = generator();
gen.math(1); // 3.141592653589793
Назначение генератора
Ранее мы использовали генератор с известным числом итераций. Но что, если мы не знаем, сколько раз повторять? Чтобы исправить это, необходимо создать бесконечный цикл в функции-генераторе. Ниже приведен пример функции, возвращающей случайное число:
function * randomFrom(...arr) {
while (true)
yield arr[Math.floor(Math.random() * arr.length)];
}
const getRandom = randomFrom(1, 2, 5, 9, 4);
getRandom.next().value; // 返回随机的一个数
Это простой пример. В качестве примера возьмем более сложные функции: мы хотим написать функцию дросселя. Если вы еще не знаете, что такое функция газа, см.эта статья.
function * throttle(func, time) {
let timerID = null;
function throttled(arg) {
clearTimeout(timerID);
timerID = setTimeout(func.bind(window, arg), time);
}
while (true)
throttled(yield);
}
const thr = throttle(console.log, 1000);
thr.next(); // {value: undefined, done: false}
thr.next('hello'); // 返回 {value: undefined, done: false} ,然后 1 秒后输出 'hello'
Есть ли лучшие примеры использования генераторов? Если вы знаете рекурсию, вы наверняка слышалиПоследовательность Фибоначчи. Обычно мы используем рекурсию для решения этой проблемы, но с генераторами мы можем написать:
function * fibonacci(seed1, seed2) {
while (true) {
yield (() => {
seed2 = seed2 + seed1;
seed1 = seed2 - seed1;
return seed2;
})();
}
}
const fib = fibonacci(0, 1);
fib.next(); // {value: 1, done: false}
fib.next(); // {value: 2, done: false}
fib.next(); // {value: 3, done: false}
fib.next(); // {value: 5, done: false}
fib.next(); // {value: 8, done: false}
Рекурсия больше не нужна! Мы можем получить следующее число в последовательности, когда нам это нужно.
Использование генераторов с HTML
Поскольку мы обсуждаем JavaScript, очевидно, что для манипулирования HTML можно использовать генераторы.
Предположим, есть какие-то HTML-блоки, которые нужно обработать, чего можно легко добиться с помощью генераторов. (конечно, есть много способов сделать это помимо генераторов)
Мы можем сделать это с помощью небольшого кода.
const strings = document.querySelectorAll('.string');
const btn = document.querySelector('#btn');
const className = 'darker';
function * addClassToEach(elements, className) {
for (const el of Array.from(elements))
yield el.classList.add(className);
}
const addClassToStrings = addClassToEach(strings, className);
btn.addEventListener('click', (el) => {
if (addClassToStrings.next().done)
el.target.classList.add(className);
});
Всего 5 строк логического кода.
Суммировать
Есть больше используют генератор. Например, при выполнении асинхронной операции или циклического генератора требуют также полезны.
Я надеюсь, что эта статья поможет вам лучше понять генератор JavaScript.
Программа перевода самородковэто сообщество, которое переводит высококачественные технические статьи из Интернета сНаггетсДелитесь статьями на английском языке на . Охват контентаAndroid,iOS,внешний интерфейс,задняя часть,блокчейн,товар,дизайн,искусственный интеллектЕсли вы хотите видеть более качественные переводы, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.