Читатели като вас помагат в подкрепа на MUO. Когато правите покупка чрез връзки на нашия сайт, ние може да спечелим комисионна за партньор. Прочетете още.

Итерирането на колекции от данни с помощта на традиционни цикли може бързо да стане тромаво и бавно, особено когато се работи с огромни количества данни.

Генераторите и итераторите на JavaScript предоставят решение за ефективно итериране на големи колекции от данни. Използвайки ги, можете да контролирате потока на итерация, да извеждате стойности една по една и да поставяте на пауза и възобновявате процеса на итерация.

Тук ще разгледате основите и вътрешността на JavaScript итератор и как можете да генерирате итератор ръчно и с помощта на генератор.

Итератори на JavaScript

Итераторът е JavaScript обект, който имплементира протокола на итератора. Тези обекти правят това, като имат a следващия метод. Този метод връща обект, който имплементира IteratorResult интерфейс.

The IteratorResult интерфейсът включва две свойства: Свършен и стойност. The

instagram viewer
Свършен свойството е булево, което връща невярно ако итераторът може да произведе следващата стойност в своята последователност или вярно ако итераторът е завършил своята последователност.

The стойност е JavaScript стойност, върната от итератора по време на неговата последователност. Когато итератор завърши своята последователност (когато Свършенвярно), това свойство се връща недефиниран.

Както подсказва името, итераторите ви позволяват да „итерирате“ обекти на JavaScript като масиви или карти. Това поведение е възможно поради итерируемия протокол.

В JavaScript итерируемият протокол е стандартен начин за дефиниране на обекти, които можете да итерирате, като например в за...на цикъл.

Например:

конст плодове = ["Банан", "манго", "ябълка", "Грозде"];

за (конст итератор на плодове) {
конзола.log (итератор);
}

/*
банан
Манго
Ябълка
грозде
*/

Този пример повтаря плодове масив с помощта на a за...на цикъл. При всяка итерация той регистрира текущата стойност в конзолата. Това е възможно, защото масивите могат да се повтарят.

Някои типове JavaScript, като масиви, низове, Комплекти и карти, са вградени итерируеми, защото те (или един от обектите нагоре в тяхната прототипна верига) имплементират @@итератор метод.

Други типове, като например обекти, не могат да се повтарят по подразбиране.

Например:

конст iterObject = {
автомобили: ["тесла", "BMW", "Тойота"],
животни: ["котка", "куче", "Хамстер"],
храна: ["Бургери", "пица", "Паста"],
};

за (конст итератор на iterObject) {
конзола.log (итератор);
}

// TypeError: iterObject не може да се повтори

Този пример демонстрира какво се случва, когато се опитате да повторите обект, който не може да бъде итериран.

Създаване на итерируем обект

За да направите обект итерируем, трябва да имплементирате a Symbol.iterator метод върху обекта. За да стане итерируем, този метод трябва да върне обект, който имплементира IteratorResult интерфейс.

The Symbol.iterator символ служи за същата цел като @@итератор и могат да се използват взаимозаменяемо в „спецификация“, но не и в код като @@итератор не е валиден синтаксис на JavaScript.

Кодовите блокове по-долу предоставят пример за това как да направите обект итерируем с помощта на iterObject.

Първо добавете Symbol.iterator метод за iterObject използвайки функция декларация.

Така:

iterObject[Символ.iterator] = функция () {
// Следващите кодови блокове отиват тук...
}

След това ще трябва да получите достъп до всички ключове в обекта, който искате да направите итерируем. Можете да получите достъп до клавишите, като използвате Обект.ключове метод, който връща масив от изброими свойства на обект. За да върнете масив от iterObject’s keys, pass the това ключова дума като аргумент за Обект.ключове.

Например:

позволявам objProperties = Обект.keys(това)

Достъпът до този масив ще ви позволи да дефинирате итерационното поведение на обекта.

След това трябва да следите итерациите на обекта. Можете да постигнете това с помощта на променливи на брояча.

Например:

позволявам propertyIndex = 0;
позволявам childIndex = 0;

Ще използвате първата променлива на брояча, за да следите свойствата на обекта, а втората, за да следите децата на свойството.

След това ще трябва да внедрите и върнете следващия метод.

Така:

връщане {
следващия() {
// Следващите кодови блокове отиват тук...
}
}

Вътре в следващия метод, ще трябва да се справите с краен случай, който възниква, когато целият обект е итериран. За да се справите с крайния случай, трябва да върнете обект с стойност настроен на недефиниран и Свършен настроен на вярно.

Ако този случай не се обработи, опитът за повторение на обекта ще доведе до безкраен цикъл.

Ето как да се справите с крайния случай:

ако (propertyIndex > objProperties.дължина- 1) {
връщане {
стойност: недефиниран,
Свършен: вярно,
};
}

След това ще трябва да получите достъп до свойствата на обекта и техните дъщерни елементи, като използвате променливите на брояча, които сте декларирали по-рано.

Така:

// Достъп до родителски и дъщерни свойства
конст свойства = това[objProperties[propertyIndex]];

конст свойство = свойства[childIndex];

След това трябва да приложите някаква логика за увеличаване на променливите на брояча. Логиката трябва да нулира childIndex когато няма повече елементи в масива на свойство и се преместете към следващото свойство в обекта. Освен това трябва да се увеличи childIndex, ако все още има елементи в масива на текущото свойство.

Например:

// Логика за увеличаване на индекса
if (childIndex >= properties.length - 1) {
// ако няма повече елементи в дъщерния масив
// нулиранедетеиндекс
childIndex = 0;

// Преминаване към следващото свойство
propertyIndex++;
} друго {
// Преминаване към следващия елемент в дъщерния масив
childIndex++
}

Накрая върнете обект с Свършен свойство, зададено на невярно и на стойност свойство, зададено на текущия дъщерен елемент в итерацията.

Например:

връщане {
Свършен: невярно,
стойност: собственост,
};

Вие завършихте Symbol.iterator функцията трябва да е подобна на кодовия блок по-долу:

iterObject[Символ.iterator] = функция () {
конст objProperties = Обект.keys(това);
позволявам propertyIndex = 0;
позволявам childIndex = 0;

връщане {
следващия: () => {
//Обработка на крайния случай
ако (propertyIndex > objProperties.дължина- 1) {
връщане {
стойност: недефиниран,
Свършен: вярно,
};
}

// Достъп до родителски и дъщерни свойства
конст свойства = това[objProperties[propertyIndex]];

конст свойство = свойства[childIndex];

// Логика за увеличаване на индекса
if (childIndex >= properties.length - 1) {
// ако няма повече елементи в дъщерния масив
// нулиранедетеиндекс
childIndex = 0;

// Преминаване към следващото свойство
propertyIndex++;
} друго {
// Преминаване към следващия елемент в дъщерния масив
childIndex++
}

връщане {
Свършен: невярно,
стойност: собственост,
};
},
};
};

Изпълнение на a за...на примка на iterObject след тази реализация няма да генерира грешка, тъй като прилага a Symbol.iterator метод.

Ръчното внедряване на итератори, както направихме по-горе, не се препоръчва, тъй като е много податливо на грешки и логиката може да бъде трудна за управление.

JavaScript генератори

Генераторът на JavaScript е функция, която можете да поставите на пауза и да възобновите нейното изпълнение по всяко време. Това поведение му позволява да произвежда последователност от стойности във времето.

Генерираща функция, която е функция, която връща генератор, предоставя алтернатива на създаването на итератори.

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

Например:

функция* пример () {
връщане"генератор"
}

Когато извикате нормална функция в JavaScript, тя връща стойността, зададена от нейния връщане ключова дума или недефиниран в противен случай. Но генераторната функция не връща никаква стойност веднага. Той връща обект Generator, който можете да присвоите на променлива.

За достъп до текущата стойност на итератора извикайте следващия метод на обекта Generator.

Например:

конст gen = пример();

console.log (gen.next()); // { стойност: "Генератор", Свършен: вярно }

В примера по-горе, стойност собственост идва от a връщане ключова дума, която ефективно прекратява генератора. Това поведение обикновено е нежелателно при функциите на генератора, тъй като това, което ги отличава от нормалните функции, е възможността за пауза и рестартиране на изпълнението.

Ключова дума за доходност

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

Например:

функция* пример() {
добив"Модел S"
добив"Модел X"
добив"Кибер камион"

връщане"тесла"
}

конст gen = пример();

console.log (gen.next()); // { стойност: "Модел S", Свършен: невярно }

В горния пример, когато следващия методът се извиква на пример генератор, той ще спира всеки път, когато срещне добив ключова дума. The Свършен свойството също ще бъде зададено на невярно докато не срещне a връщане ключова дума.

Обаждане на следващия метод няколко пъти на пример генератор, за да демонстрирате това, ще имате следния резултат.

console.log (gen.next()); // { стойност: „Модел X“, Свършен: невярно }
console.log (gen.next()); // { стойност: „Кибер камион“, Свършен: невярно }
console.log (gen.next()); // { стойност: "тесла", Свършен: вярно }

конзола.log (gen.next()); // { стойност: недефинирано, готово: вярно }

Можете също така да итерирате върху обект Generator, като използвате за...на цикъл.

Например:

за (конст итератор на gen) {
конзола.log (итератор);
}

/*
Модел S
Модел X
Кибер камион
*/

Използване на итератори и генератори

Въпреки че итераторите и генераторите може да изглеждат като абстрактни концепции, те не са. Те могат да бъдат полезни при работа с безкрайни потоци от данни и колекции от данни. Можете също да ги използвате за създаване на уникални идентификатори. Библиотеките за управление на състоянието като MobX-State-Tree (MST) също ги използват под капака.