diff --git a/1-js/05-data-types/06-iterable/article.md b/1-js/05-data-types/06-iterable/article.md index 37d7e31e5..286eb1d9c 100644 --- a/1-js/05-data-types/06-iterable/article.md +++ b/1-js/05-data-types/06-iterable/article.md @@ -1,20 +1,20 @@ -# Iterables +# Ітеративні об’єкти -*Iterable* objects are a generalization of arrays. That's a concept that allows us to make any object useable in a `for..of` loop. +*Ітеративні* об’єкти є узагальненням масивів. Це концепція, яка дозволяє нам зробити будь-який об’єкт придатним для використання в циклі `for..of`. -Of course, Arrays are iterable. But there are many other built-in objects, that are iterable as well. For instance, strings are also iterable. +Звичайно, по масивах можна ітеруватися. Але є багато інших вбудованих об’єктів, які також можна ітерувати. Наприклад, рядки також можна ітерувати. -If an object isn't technically an array, but represents a collection (list, set) of something, then `for..of` is a great syntax to loop over it, so let's see how to make it work. +Якщо об’єкт технічно не є масивом, а представляє колекцію (list, set) чогось, то `for..of` -- чудовий синтаксис для його обходу, тому подивімось, як змусити його працювати. ## Symbol.iterator -We can easily grasp the concept of iterables by making one of our own. +Ми можемо легко зрозуміти концепцію ітеративних об’єктів, зробивши її власноруч. -For instance, we have an object that is not an array, but looks suitable for `for..of`. +Наприклад, у нас є об’єкт, який не є масивом, але виглядає придатним для `for..of`. -Like a `range` object that represents an interval of numbers: +Як, наприклад, об’єкт `range`, який представляє інтервал чисел: ```js let range = { @@ -22,18 +22,18 @@ let range = { to: 5 }; -// We want the for..of to work: +// Ми хочемо, щоб for..of працював: // for(let num of range) ... num=1,2,3,4,5 ``` -To make the `range` object iterable (and thus let `for..of` work) we need to add a method to the object named `Symbol.iterator` (a special built-in symbol just for that). +Щоб зробити об’єкт `range` ітерабельним (і таким чином дозволити `for..of` працювати), нам потрібно додати метод до об’єкта з назвою `Symbol.iterator` (спеціальний вбудований символ саме для цього). -1. When `for..of` starts, it calls that method once (or errors if not found). The method must return an *iterator* -- an object with the method `next`. -2. Onward, `for..of` works *only with that returned object*. -3. When `for..of` wants the next value, it calls `next()` on that object. -4. The result of `next()` must have the form `{done: Boolean, value: any}`, where `done=true` means that the iteration is finished, otherwise `value` is the next value. +1. Коли `for..of` запускається, він викликає цей метод один раз (або викликає помилку, якщо цей метод не знайдено). Метод повинен повернути *ітератор* -- об’єкт з методом `next`. +2. Далі `for..of` працює *лише з поверненим об’єктом*. +3. Коли `for..of` хоче отримати наступне значення, він викликає `next()` на цьому об’єкті. +4. Результат `next()` повинен мати вигляд `{done: Boolean, value: any}`, де `done=true` означає, що ітерація завершена, інакше `value` -- це наступне значення. -Here's the full implementation for `range` with remarks: +Ось повна реалізація об’єкту `range` із зауваженнями: ```js run let range = { @@ -41,18 +41,18 @@ let range = { to: 5 }; -// 1. call to for..of initially calls this +// 1. виклик for..of спочатку викликає цю функцію range[Symbol.iterator] = function() { - // ...it returns the iterator object: - // 2. Onward, for..of works only with this iterator, asking it for next values + // ...вона повертає об’єкт ітератора: + // 2. Далі, for..of працює тільки з цим ітератором, запитуючи у нього наступні значення return { current: this.from, last: this.to, - // 3. next() is called on each iteration by the for..of loop + // 3. next() викликається на кожній ітерації циклом for..of next() { - // 4. it should return the value as an object {done:.., value :...} + // 4. він повинен повертати значення як об’єкт {done:.., value :...} if (this.current <= this.last) { return { done: false, value: this.current++ }; } else { @@ -62,22 +62,22 @@ range[Symbol.iterator] = function() { }; }; -// now it works! +// тепер це працює! for (let num of range) { - alert(num); // 1, then 2, 3, 4, 5 + alert(num); // 1, потім 2, 3, 4, 5 } ``` -Please note the core feature of iterables: separation of concerns. +Будь ласка, зверніть увагу на основну особливість ітеративних об’єктів: розділення проблем. -- The `range` itself does not have the `next()` method. -- Instead, another object, a so-called "iterator" is created by the call to `range[Symbol.iterator]()`, and its `next()` generates values for the iteration. +- Сам `range` не має методу `next()`. +- Натомість інший об’єкт, так званий "ітератор", створюється за допомогою виклику `range[Symbol.iterator]()`, а його `next()` генерує значення для ітерації. -So, the iterator object is separate from the object it iterates over. +Отже, об’єкт, що ітерує відокремлений від об’єкта, який він ітерує. -Technically, we may merge them and use `range` itself as the iterator to make the code simpler. +Технічно, ми можемо об’єднати їх і використовувати `range` в якості ітератора, щоб зробити код простішим. -Like this: +Подібно до цього: ```js run let range = { @@ -99,55 +99,55 @@ let range = { }; for (let num of range) { - alert(num); // 1, then 2, 3, 4, 5 + alert(num); // 1, тоді 2, 3, 4, 5 } ``` -Now `range[Symbol.iterator]()` returns the `range` object itself: it has the necessary `next()` method and remembers the current iteration progress in `this.current`. Shorter? Yes. And sometimes that's fine too. +Тепер `range[Symbol.iterator]()` повертає сам об’єкт `range`: він має необхідний `next()` метод і пам’ятає поточну ітерацію прогресу в `this.current`. Коротше? Так. А іноді це також добре. -The downside is that now it's impossible to have two `for..of` loops running over the object simultaneously: they'll share the iteration state, because there's only one iterator -- the object itself. But two parallel for-ofs is a rare thing, even in async scenarios. +Недоліком є те, що тепер неможливо мати два `for..of` цикли паралельно для проходження через об’єкт: вони будуть ділити ітераційний стан, тому що є тільки один ітератор -- сам об’єкт. Але два паралельних for-of це рідкісний випадок, навіть у асинхронізованих сценаріях. ```smart header="Infinite iterators" -Infinite iterators are also possible. For instance, the `range` becomes infinite for `range.to = Infinity`. Or we can make an iterable object that generates an infinite sequence of pseudorandom numbers. Also can be useful. +Також можливі нескінченні ітератори. Наприклад, `range` стає нескінченним для `range.to = Infinity`. Або ми можемо зробити ітерований об’єкт, який генерує нескінченну послідовність псевдорандомних чисел. Це також може бути корисним. -There are no limitations on `next`, it can return more and more values, that's normal. +Немає обмежень на `next`, він може повертати все більше і більше значень, це нормально. -Of course, the `for..of` loop over such an iterable would be endless. But we can always stop it using `break`. +Звичайно, `for..of` цикли через такий об’єкт буде нескінченним. Але ми завжди можемо зупинити його за допомогою `break`. ``` -## String is iterable +## Рядок є ітерованим -Arrays and strings are most widely used built-in iterables. +Масиви та рядки найбільш широко використовуються вбудовані ітератори. -For a string, `for..of` loops over its characters: +Для рядка, `for..of` цикл проходить по символам: ```js run for (let char of "test") { - // triggers 4 times: once for each character - alert( char ); // t, then e, then s, then t + // викликається 4 рази: один раз для кожного символу + alert( char ); // t, потім e, потім s, потім t } ``` -And it works correctly with surrogate pairs! +І це правильно працює з сурогатними парами! ```js run let str = '𝒳😂'; for (let char of str) { - alert( char ); // 𝒳, and then 😂 + alert( char ); // 𝒳, і потім 😂 } ``` -## Calling an iterator explicitly +## Виклик ітератора явно -For deeper understanding, let's see how to use an iterator explicitly. +Для глибшого розуміння, подивімось, як явно використовувати ітератор. -We'll iterate over a string in exactly the same way as `for..of`, but with direct calls. This code creates a string iterator and gets values from it "manually": +Ми будемо ітерувати рядок точно так само, як для `for..of`, але з прямими викликами. Цей код створює ітератор рядка і отримує значення від нього "вручну": ```js run -let str = "Hello"; +let str = "Привіт"; -// does the same as +// робить те ж саме, як // for (let char of str) alert(char); *!* @@ -157,97 +157,96 @@ let iterator = str[Symbol.iterator](); while (true) { let result = iterator.next(); if (result.done) break; - alert(result.value); // outputs characters one by one + alert(result.value); // виводить символи один за одним } ``` -That is rarely needed, but gives us more control over the process than `for..of`. For instance, we can split the iteration process: iterate a bit, then stop, do something else, and then resume later. +Це рідко потрібно, але дає нам більше контролю над процесом, ніж `for ..of`. Наприклад, ми можемо розділити процес ітерації: трохи ітерувати, а потім зупинитися, зробити щось інше, а потім відновити пізніше. -## Iterables and array-likes [#array-like] +## Ітеровані об’єкти та псевдомасиви [#array-like] -Two official terms look similar, but are very different. Please make sure you understand them well to avoid the confusion. +Ці два офіційних терміни виглядають подібними, але дуже різні. Будь ласка, переконайтеся, що ви добре розумієте їх, щоб уникнути плутанини. -- *Iterables* are objects that implement the `Symbol.iterator` method, as described above. -- *Array-likes* are objects that have indexes and `length`, so they look like arrays. +- *Ітеровані* -- це об’єкти, які реалізують метод `Symbol.iterator`, як описано вище. +- *Псевдомасиви* -- це об’єкти, які мають індекси та `length`, тому вони виглядають як масиви. -When we use JavaScript for practical tasks in a browser or any other environment, we may meet objects that are iterables or array-likes, or both. +Коли ми використовуємо JavaScript для практичних завдань у браузері або будь-якому іншому середовищі, ми можемо зустріти об’єкти, які є ітерованими або масивами, або обома. +Наприклад, рядки є ітерованими об’єктами (`for..of` працює на них) та псевдомасивами (у них є числові індекси та `length`). -For instance, strings are both iterable (`for..of` works on them) and array-like (they have numeric indexes and `length`). +Але ітерований об’єкт може не бути масивом. І навпаки, псевдомасив може бути не ітерованим об’єктом. -But an iterable may be not array-like. And vice versa an array-like may be not iterable. +Наприклад, `range` у прикладі вище є ітерованим об’єктом, але не масивом, тому що він не має індексованих властивостей та `length`. -For example, the `range` in the example above is iterable, but not array-like, because it does not have indexed properties and `length`. - -And here's the object that is array-like, but not iterable: +І ось об’єкт, який є псевдомасивом, але не ітерованим об’єктом: ```js run -let arrayLike = { // has indexes and length => array-like +let arrayLike = { // має індекси та length => псевдомасив 0: "Hello", 1: "World", length: 2 }; *!* -// Error (no Symbol.iterator) +// Помилка (немає Symbol.iterator) for (let item of arrayLike) {} */!* ``` -Both iterables and array-likes are usually *not arrays*, they don't have `push`, `pop` etc. That's rather inconvenient if we have such an object and want to work with it as with an array. E.g. we would like to work with `range` using array methods. How to achieve that? +Обидва, ітерований об’єкт та псевдомасив, як правило є *не масивами*, вони не мають `push`,` pop` та ін. Це досить незручно, якщо у нас є такий об’єкт і ми хочемо працювати з ним як з масивом. Наприклад, ми хотіли б працювати з `angy` за допомогою методів масиву. Як цього досягти? ## Array.from -There's a universal method [Array.from](mdn:js/Array/from) that takes an iterable or array-like value and makes a "real" `Array` from it. Then we can call array methods on it. +Існує універсальний метод [Array.from](mdn:js/Array/from), який приймає ітерований об’єкт або псевдомасив і робить з нього "справжній" масив. Тоді ми можемо викликати на ньому методи масиву. -For instance: +Наприклад: ```js run let arrayLike = { - 0: "Hello", - 1: "World", + 0: "Привіт", + 1: "Світ", length: 2 }; *!* let arr = Array.from(arrayLike); // (*) */!* -alert(arr.pop()); // World (method works) +alert(arr.pop()); // Світ (метод працює) ``` -`Array.from` at the line `(*)` takes the object, examines it for being an iterable or array-like, then makes a new array and copies all items to it. +`Array.from` у рядку `(*)` бере об’єкт, перевіряє його на ітерабельність або те, що це псевдомасив, потім створює новий масив і копіює до нього всі елементи. -The same happens for an iterable: +Те ж саме відбувається і з ітерованим об’єктом: ```js -// assuming that range is taken from the example above +// припустимо, що діапазон взятий з наведеного вище прикладу let arr = Array.from(range); alert(arr); // 1,2,3,4,5 (array toString conversion works) ``` -The full syntax for `Array.from` also allows us to provide an optional "mapping" function: +Повний синтаксис для `Array.from` також дозволяє нам надати додаткову функцію "трансформації": ```js Array.from(obj[, mapFn, thisArg]) ``` The optional second argument `mapFn` can be a function that will be applied to each element before adding it to the array, and `thisArg` allows us to set `this` for it. -For instance: +Наприклад: ```js -// assuming that range is taken from the example above +// припустимо, що діапазон взятий з наведеного вище прикладу -// square each number +// порахуємо квадрат кожного числа let arr = Array.from(range, num => num * num); alert(arr); // 1,4,9,16,25 ``` -Here we use `Array.from` to turn a string into an array of characters: +Тут ми використовуємо `Array.from`, щоб перетворити рядок у масив символів: ```js run let str = '𝒳😂'; -// splits str into array of characters +// розіб’ємо рядок на масив символів let chars = Array.from(str); alert(chars[0]); // 𝒳 @@ -255,14 +254,14 @@ alert(chars[1]); // 😂 alert(chars.length); // 2 ``` -Unlike `str.split`, it relies on the iterable nature of the string and so, just like `for..of`, correctly works with surrogate pairs. +На відміну від `str.split`, він спирається на ітерабельний характер рядка і тому, так само, як `for..of`, коректно працює з сурогатними парами. -Technically here it does the same as: +Технічно тут це відбувається так само, як: ```js run let str = '𝒳😂'; -let chars = []; // Array.from internally does the same loop +let chars = []; // Array.from внутрішньо робить цей самий цикл for (let char of str) { chars.push(char); } @@ -270,9 +269,9 @@ for (let char of str) { alert(chars); ``` -...But it is shorter. +...Але це коротше. -We can even build surrogate-aware `slice` on it: +Ми навіть можемо побудувати на ньому `slice`, що підтримує сурогатні пари: ```js run function slice(str, start, end) { @@ -283,25 +282,26 @@ let str = '𝒳😂𩷶'; alert( slice(str, 1, 3) ); // 😂𩷶 -// the native method does not support surrogate pairs -alert( str.slice(1, 3) ); // garbage (two pieces from different surrogate pairs) +// нативний метод не підтримує сурогатні пари +alert( str.slice(1, 3) ); // сміття (дві частини з різних сурогатних пар) ``` -## Summary +## Підсумки -Objects that can be used in `for..of` are called *iterable*. +Об’єкти, які можна використовуватися у `for..of`, називаються *ітерованими*. -- Technically, iterables must implement the method named `Symbol.iterator`. - - The result of `obj[Symbol.iterator]()` is called an *iterator*. It handles further iteration process. +- Технічно ітеровані об’єкти повинні реалізовувати метод з назвою `Symbol.iterator`. + - Результат `obj[Symbol.iterator]()` називається *ітератором*. Він забезпечує подальший процес ітерації. - An iterator must have the method named `next()` that returns an object `{done: Boolean, value: any}`, here `done:true` denotes the end of the iteration process, otherwise the `value` is the next value. -- The `Symbol.iterator` method is called automatically by `for..of`, but we also can do it directly. -- Built-in iterables like strings or arrays, also implement `Symbol.iterator`. -- String iterator knows about surrogate pairs. + - Ітератор повинен мати метод з назвою `next()`, який повертає об’єкт `{done: Boolean, value: any}`, де `done: true` означає кінець процесу ітерації, інакше `value` є наступним значенням. +- Метод `Symbol.iterator` автоматично викликається `for..of`, але ми також можемо це зробити безпосередньо. +- Вбудовані ітеровані об’єкти, такі як рядки або масиви, також реалізують `Symbol.iterator`. +- Рядковий ітератор знає про сурогатні пари. -Objects that have indexed properties and `length` are called *array-like*. Such objects may also have other properties and methods, but lack the built-in methods of arrays. +Об’єкти, які мають індексовані властивості та `length`, називаються *псевдомасивами*. Такі об’єкти також можуть мати інші властивості та методи, але не мають вбудованих методів масивів. -If we look inside the specification -- we'll see that most built-in methods assume that they work with iterables or array-likes instead of "real" arrays, because that's more abstract. +Якщо ми заглянемо в специфікацію -- ми побачимо, що більшість вбудованих методів припускають, що вони працюють з ітерованими об’єктами або псевдомасивами замість "реальних" масивів, тому що це більш абстрактно. -`Array.from(obj[, mapFn, thisArg])` makes a real `Array` from an iterable or array-like `obj`, and we can then use array methods on it. The optional arguments `mapFn` and `thisArg` allow us to apply a function to each item. +`Array.from(obj[, mapFn, thisArg])` створює справжній `Array` з ітерованого об’єкту або псевдомасиву `obj`, і тоді ми можемо використовувати на ньому методи масиву. Необов’язкові аргументи `mapFn` та` thisArg` дозволяють нам застосовувати функції до кожного елемента.
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: