
Итераторы введены в ES6 для итерации коллекций в JavaScript.
Задумывались ли вы, как цикл for of или деструктуризация работает за кулисами для перебора отдельных значений. Это из-за итераторов!!!
Итераторы реализованы для следующих типов,
· Множество
· Нить
· Карта
· Набор
В основном мы используем массивы и строки, поэтому мы можем увидеть несколько примеров с этими типами.
Рассмотрим итерацию массива имен ниже через цикл for of
const names = ["John", "David", "Will", "James"];
for (const name of names) {
console.log(name);
}
// O/P:
// John
// David
// Will
// James
Точно так же, если нам нужно напечатать каждый из символов в строке, мы можем использовать один и тот же цикл for of для его вывода.
for (const char of "John") {
console.log(char);
}
// O/P:
// J
// O
// H
// N
Итак, что если нам нужно напечатать значения объекта, как показано ниже
const wishList = {
clothes: ["Shirt - 1 ", "Pant - 1"],
electronics: ["Mobile", "Laptop"],
others: ["Speakers", "Chair"],
};
for (const category of wishList) {
console.log(category);
}
//O/P
Uncaught TypeError: wishList is not iterable
at <anonymous>:7:24
Это вызовет ошибку «Ошибка: Uncaught TypeError: список желаний не является итерируемым».
Что означает список желаний не является итерируемым?
А вот и концепция итерируемых объектов и итераторов. В основном цикл for of может повторять тип, который реализует эту концепцию итераторов.
Что такое концепция итераторов, как мы можем реализовать ее в приведенном выше примере?
Есть два способа получить желаемый результат печати элементов списка желаний.
- Написание собственного метода для возврата элементов
const wishList = {
clothes: ["Shirt - 1 ", "Pant - 1"],
electronics: ["Mobile", "Laptop"],
others: ["Speakers", "Chair"],
getWishList() {
return [...this.clothes, ...this.electronics, ...this.others];
},
};
for (const category of wishList.getWishList()) {
console.log(category);
}
// O/P:
// Shirt - 1
// Pant - 1
// Mobile
// Laptop
// Speakers
// Chair
Этот метод имеет некоторые проблемы,
· Имя очень специфично для этого сценария, это может быть getWishLists, retrieveWishList и n число возможностей для имени, и разработчик должен знать это пользовательское имя, чтобы получить данные вместо встроенной функциональности в случае массива или строки
· Тип возвращаемого значения в настоящее время является строкой, как мы знаем об этом объекте, но что, если в будущем какой-нибудь другой разработчик изменит тип возвращаемого значения, например [{имя: «Рубашка — 1» }, {имя: «Пант — 1»}], поэтому тип возвращаемого значения также непредсказуем.
Мы можем достичь решения, учитывая вышеуказанные проблемы, используя итераторы.
const simpleIterableObject = {
[Symbol.iterator]() {
let count = 0;
const iterator = {
next() {
if (count === 0) {
count++;
return {
value: "I'm Iterable",
done: false,
};
} else {
return {
value: undefined,
done: true,
};
}
},
};
return iterator;
},
};
for (const iteratorObj of simpleIterableObject) {
console.log(iteratorObj);
}
//O/P
// I'm Iterable
Структура данных, которая считается итерируемой, если она возвращает «объект итератора», в котором будет метод «next()», и вызов этого метода next() должен возвращать объект со свойствами «value» и «done».
· [Symbol.iterator]() — это уникальный ключ, который не будет переопределять существующие ключи объекта, он вернет объект итератора, который используется циклом for of и будет ли следующий метод внутри объекта итератора
· { value: «I’m Iterable», done: true — это общая структура для всех итерируемых типов.
«значение» — это свойство будет содержать точное значение, которое должно быть доступно в цикле for of.
«done» — этот логический флаг обозначает, завершена ли итерация или нет, если он ложный, он выдаст свойство value внутри цикла, если он ложный, будет считаться, что итерация завершена
Таким образом, вышеуказанные проблемы с пользовательским именем решаются с помощью «[Symbol.iterator()]», а тип возвращаемого значения становится предсказуемым с использованием структуры объекта «{ значение: ‹‹значение››, выполнено:‹‹флаг››}»
Итак, вот полный код для итерации нашего объекта «список желаний».
const wishList = {
clothes: ["Shirt - 1 ", "Pant - 1"],
electronics: ["Mobile", "Laptop"],
others: ["Speakers", "Chair"],
[Symbol.iterator]() {
let categoryIndex = 0;
let productIndex = 0;
const values = Object.values(this);
let products;
const iterator = {
next() {
if (!values.length) {
return {
value: undefined,
done: true,
};
}
// Get the next products in the category once all the products in the previous categories are iterated
if (!products || productIndex === products.length) {
products = values[categoryIndex];
productIndex = 0;
categoryIndex++;
}
//Complete the loop if all the categories are iterated
if (values.length < categoryIndex) {
return {
value: undefined,
done: true,
};
}
//Iterate the products in each
while (productIndex < products.length) {
return {
value: products[productIndex++],
done: false,
};
}
},
};
return iterator;
},
};
for (const iteratorObj of wishList) {
console.log(iteratorObj);
}
Таким образом, мы можем имитировать поведение по умолчанию обычных итерируемых типов, таких как Array и String.
Примечание. Мы также можем назначить функцию-генератор свойству [Symbol.iterator]() объекта, так как по умолчанию он возвращает объект-итератор с функцией next(), а вызов метода next() возвращает аналогичная структура, как указано выше.
Удачного кодирования!!!