diff options
Diffstat (limited to 'files/uk/web/javascript/reference/iteration_protocols/index.html')
-rw-r--r-- | files/uk/web/javascript/reference/iteration_protocols/index.html | 353 |
1 files changed, 353 insertions, 0 deletions
diff --git a/files/uk/web/javascript/reference/iteration_protocols/index.html b/files/uk/web/javascript/reference/iteration_protocols/index.html new file mode 100644 index 0000000000..8ac04e5169 --- /dev/null +++ b/files/uk/web/javascript/reference/iteration_protocols/index.html @@ -0,0 +1,353 @@ +--- +title: Протоколи перебору +slug: Web/JavaScript/Reference/Iteration_protocols +tags: + - ECMAScript 2015 + - JavaScript + - Ітератор + - ітерабельний об'єкт +translation_of: Web/JavaScript/Reference/Iteration_protocols +original_slug: Web/JavaScript/Reference/Протоколи_перебору +--- +<div>{{jsSidebar("More")}}</div> + +<p>Пара доповнень до ECMAScript 2015 є не новими вбудованими елементами чи синтаксисом, а протоколами. Ці протоколи можуть реалізовуватись будь-яким об'єктом, що відповідає певним правилам.</p> + +<p>Існують два протоколи: <a href="#Протокол_перебируваного">протокол ітерабельного об'єкта</a> і <a href="#Протокол_перебирача">протокол ітератора</a>.</p> + +<h2 id="Протокол_ітерабельного_обєкта">Протокол ітерабельного об'єкта</h2> + +<p>Протокол <strong>ітерабельного об'єкта</strong> дозволяє об'єктам JavaScript визначати чи налаштовувати свою ітераційну поведінку, наприклад, через які значення буде проходити цикл у конструкції {{jsxref("Statements/for...of", "for..of")}}. Деякі вбудовані типи є <a href="#Built-in_iterables">вбудованими ітерабельними об'єктами</a> з визначеною за замовчуванням ітераційною поведінкою, наприклад, {{jsxref("Array")}} або {{jsxref("Map")}}, в той час, як інші типи (такі, як {{jsxref("Object")}}) не є ітерабельними.</p> + +<p>Для того, щоб бути <strong>ітерабельним</strong>, об'єкт має реалізувати метод <strong>@@iterator</strong>, тобто, цей об'єкт (або один з об'єктів у його <a href="/uk/docs/Web/JavaScript/Inheritance_and_the_prototype_chain">ланцюжку прототипів</a>) повинен мати властивість з ключем <strong>@@iterator</strong>, доступну через константу <code>{{jsxref("Symbol.iterator")}}</code>:</p> + +<table class="standard-table"> + <thead> + <tr> + <th scope="col">Властивість</th> + <th scope="col">Значення</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>[Symbol.iterator]</code></td> + <td>Функція без аргументів, яка повертає об'єкт, що відповідає <a href="#The_iterator_protocol">протоколу ітератора</a>.</td> + </tr> + </tbody> +</table> + +<p>Коли виникає необхідність перебрати об'єкт (наприклад, на початку циклу <code>for..of</code>), його метод <code>@@iterator</code> викликається без аргументів, а <strong>ітератор</strong>, який він повертає, використовується для отримання значень, що перебираються.</p> + +<h2 id="Протокол_ітератора">Протокол ітератора</h2> + +<p>Протокол <strong>ітератора</strong> визначає стандартний спосіб створювати послідовності значень (скінченні або нескінченні).</p> + +<p>Об'єкт є ітератором, коли реалізує метод <code><strong>next()</strong></code> з наступною семантикою:</p> + +<table class="standard-table"> + <tbody> + <tr> + <th scope="col">Властивість</th> + <th scope="col">Значення</th> + </tr> + <tr> + <td><code>next</code></td> + <td> + <p>Функція з нулем аргументів, яка повертає об'єкт з двома властивостями:</p> + + <ul> + <li><code>done</code> (булеве значення) + + <ul> + <li>Має значення <code>true</code>, якщо ітератор досяг кінця послідовності, що перебирається. В цьому випадку <code>value</code> може містити <em>значення, що повертається</em> ітератором. Значення, що повертаються, пояснюються <a href="http://www.2ality.com/2013/06/iterators-generators.html#generators-as-threads">тут</a>.</li> + <li>Має значення <code>false</code>, якщо ітератор був здатний надати наступне значення послідовності. Це аналогічно тому, щоб взагалі не вказувати значення властивості <code>done</code>.</li> + </ul> + </li> + <li><code>value</code> - будь-яке значення JavaScript, що повертає ітератор. Його можна не вказувати, коли <code>done</code> дорівнює <code>true</code>.</li> + </ul> + + <p>Метод <code>next</code> завжди повинен повертати об'єкт з належними властивостями, в тому числі <code>done</code> та <code>value</code>. Якщо повертається значення, що не є об'єктом (наприклад, <code>false</code> чи <code>undefined</code>), буде викинуто помилку {{jsxref("TypeError")}} ("iterator.next() returned a non-object value").</p> + </td> + </tr> + </tbody> +</table> + +<div class="blockIndicator note"> +<p>Неможливо знати, чи певний об'єкт реалізує протокол ітератора, однак, можна легко створити об'єкт, який відповідає обом протоколам, ітератора та ітерабельного об'єкта (як показано нижче у прикладі). Це дозволяє використовувати ітератор там, де очікується ітерабельний об'єкт. Тому нечасто є потреба реалізовувати протокол ітератора, не реалізуючи також протокол ітерабельного об'єкта. </p> + +<pre><code>var myIterator = { + next: function() { + // ... + }, + [Symbol.iterator]: function() { return this } +};</code> +</pre> +</div> + +<h2 id="Приклади_застосування_протоколів_перебору">Приклади застосування протоколів перебору</h2> + +<p>Об'єкт {{jsxref("String")}} є прикладом вбудованого ітерабельного об'єкта:</p> + +<pre class="brush: js">var someString = '13'; +typeof someString[Symbol.iterator]; // "function" +</pre> + +<p><a href="/uk/docs/Web/JavaScript/Reference/Global_Objects/String/@@iterator">Вбудований ітератор </a>об'єкта <code>String</code> повертає коди символів рядка один за одним:</p> + +<pre class="brush: js">var iterator = someString[Symbol.iterator](); +iterator + ''; // "[object String Iterator]" + +iterator.next(); // { value: "1", done: false } +iterator.next(); // { value: "3", done: false } +iterator.next(); // { value: undefined, done: true }</pre> + +<p>Деякі вбудовані конструкції, такі як <a href="/uk/docs/Web/JavaScript/Reference/Operators/Spread_syntax">оператор розпакування</a>, використовують під капотом той самий протокол перебору:</p> + +<pre class="brush: js">[...someString] // ["1", "3"]</pre> + +<p>Ми можемо перевизначити поведінку під час перебору, надавши свій власний метод <code>@@iterator</code>:</p> + +<pre class="brush: js">var someString = new String('привіт'); // необхідно явно конструювати об'єкт String, щоб запобігти автопакуванню + +someString[Symbol.iterator] = function() { + return { // це ітератор, що повертає єдиний елемент, рядок "бувай" + next: function() { + if (this._first) { + this._first = false; + return { value: 'бувай', done: false }; + } else { + return { done: true }; + } + }, + _first: true + }; +}; +</pre> + +<p>Зверніть увагу, як перевизначення методу <code>@@iterator</code> впливає на поведінку вбудованих конструкцій, що використовують протокол перебору:</p> + +<pre class="brush: js">[...someString]; // ["бувай"] +someString + ''; // "привіт" +</pre> + +<h2 id="Приклади_ітерабельних_обєктів">Приклади ітерабельних об'єктів</h2> + +<h3 id="Вбудовані_ітерабельні_обєкти">Вбудовані ітерабельні об'єкти</h3> + +<p>{{jsxref("String")}}, {{jsxref("Array")}}, {{jsxref("TypedArray")}}, {{jsxref("Map")}} та {{jsxref("Set")}} всі є вбудованими ітерабельними об'єктами, тому що кожний з їхніх прототипів реалізує метод <code>@@</code><code>iterator</code>.</p> + +<h3 id="Створені_користувачем_ітерабельні_обєкти">Створені користувачем ітерабельні об'єкти</h3> + +<p>Ми можемо створювати власні ітерабельні об'єкти наступним чином:</p> + +<pre class="brush: js">var myIterable = {}; +myIterable[Symbol.iterator] = function* () { + yield 1; + yield 2; + yield 3; +}; +[...myIterable]; // [1, 2, 3] +</pre> + +<h3 id="Вбудовані_API_що_приймають_ітерабельні_обєкти">Вбудовані API, що приймають ітерабельні об'єкти</h3> + +<p>Існує багато API, які приймають ітерабельні об'єкти, наприклад: {{jsxref("Map", "Map([iterable])")}}, {{jsxref("WeakMap", "WeakMap([iterable])")}}, {{jsxref("Set", "Set([iterable])")}} and {{jsxref("WeakSet", "WeakSet([iterable])")}}:</p> + +<pre class="brush: js">var myObj = {}; +new Map([[1, 'а'], [2, 'б'], [3, 'в']]).get(2); // "б" +new WeakMap([[{}, 'а'], [myObj, 'б'], [{}, 'в']]).get(myObj); // "б" +new Set([1, 2, 3]).has(3); // true +new Set('123').has('2'); // true +new WeakSet(function* () { + yield {}; + yield myObj; + yield {}; +}()).has(myObj); // true +</pre> + +<p>Дивіться також {{jsxref("Promise.all", "Promise.all(iterable)")}}, {{jsxref("Promise.race", "Promise.race(iterable)")}} та {{jsxref("Array.from", "Array.from()")}}.</p> + +<h3 id="Синтаксис_що_очікує_на_ітерабельний_обєкт">Синтаксис, що очікує на ітерабельний об'єкт</h3> + +<p>Деякі оператори та вирази очікують на ітерабельні об'єкти, наприклад, цикли <code><a href="/uk/docs/Web/JavaScript/Reference/Statements/for...of">for-of</a></code>, <a href="/uk/docs/Web/JavaScript/Reference/Operators/Spread_syntax">оператор розпакування</a>, <code><a href="/uk/docs/Web/JavaScript/Reference/Operators/yield*">yield*</a></code> та <a href="/uk/docs/Web/JavaScript/Reference/Operators/Деструктуризація">деструктуризаційне присвоєння</a>:</p> + +<pre class="brush: js">for(let value of ['а', 'б', 'в']){ + console.log(value); +} +// "а" +// "б" +// "в" + +[...'абв']; // ["а", "б", "в"] + +function* gen() { + yield* ['а', 'б', 'в']; +} + +gen().next(); // { value:"а", done:false } + +[a, b, c] = new Set(['а', 'б', 'в']); +a // "а" + +</pre> + +<h3 id="Погано_сформовані_ітерабельні_обєкти">Погано сформовані ітерабельні об'єкти</h3> + +<p>Якщо метод ітерабельного об'єкта <code>@@iterator</code> не повертає об'єкт ітератора, то це погано сформований ітерабельний об'єкт. Використання його в такому вигляді ймовірно призведе до викидання винятків під час виконання або помилкової поведінки:</p> + +<pre class="brush: js">var nonWellFormedIterable = {} +nonWellFormedIterable[Symbol.iterator] = () => 1 +[...nonWellFormedIterable] // TypeError: [] is not a function +</pre> + +<h2 id="Приклади_ітераторів">Приклади ітераторів</h2> + +<h3 id="Простий_ітератор">Простий ітератор</h3> + +<pre class="brush: js">function makeIterator(array) { + var nextIndex = 0; + + return { + next: function() { + return nextIndex < array.length ? + {value: array[nextIndex++], done: false} : + {done: true}; + } + }; +} + +var it = makeIterator(['yo', 'ya']); + +console.log(it.next().value); // 'yo' +console.log(it.next().value); // 'ya' +console.log(it.next().done); // true +</pre> + +<h3 id="Нескінченний_ітератор">Нескінченний ітератор</h3> + +<pre class="brush: js">function idMaker() { + var index = 0; + + return { + next: function(){ + return {value: index++, done: false}; + } + }; +} + +var it = idMaker(); + +console.log(it.next().value); // '0' +console.log(it.next().value); // '1' +console.log(it.next().value); // '2' +// ... +</pre> + +<h3 id="З_генератором">З генератором</h3> + +<pre class="brush: js">function* makeSimpleGenerator(array) { + var nextIndex = 0; + + while (nextIndex < array.length) { + yield array[nextIndex++]; + } +} + +var gen = makeSimpleGenerator(['yo', 'ya']); + +console.log(gen.next().value); // 'yo' +console.log(gen.next().value); // 'ya' +console.log(gen.next().done); // true + + + +function* idMaker() { + var index = 0; + while (true) + yield index++; +} + +var gen = idMaker(); + +console.log(gen.next().value); // '0' +console.log(gen.next().value); // '1' +console.log(gen.next().value); // '2' +// ... +</pre> + +<h3 id="З_класом_ES2015">З класом ES2015</h3> + +<pre class="brush: js">class SimpleClass { + constructor(data) { + this.index = 0; + this.data = data; + } + + [Symbol.iterator]() { + return { + next: () => { + if (this.index < this.data.length) { + return {value: this.data[this.index++], done: false}; + } else { + this.index = 0; //Якщо ми хотіли б перебрати його знову, без примусового ручного оновлення індексу + return {done: true}; + } + } + } + }; +} + +const simple = new SimpleClass([1,2,3,4,5]); + +for (const val of simple) { + console.log(val); //'0' '1' '2' '3' '4' '5' +} +</pre> + +<h2 id="Генератор_є_ітератором_чи_ітерабельним_обєктом">Генератор є ітератором чи ітерабельним об'єктом?</h2> + +<p><a href="/uk/docs/Web/JavaScript/Reference/Global_Objects/Generator">Об'єкт генератор</a> є одночасно ітератором та ітерабельним об'єктом:</p> + +<pre class="brush: js">var aGeneratorObject = function* () { + yield 1; + yield 2; + yield 3; +}(); +typeof aGeneratorObject.next; +// "function", бо він має метод next, отже, він ітератор +typeof aGeneratorObject[Symbol.iterator]; +// "function", бо він має метод @@iterator, отже, він ітерабельний об'єкт +aGeneratorObject[Symbol.iterator]() === aGeneratorObject; +// true, бо його метод @@iterator повертає себе (ітератор), +// отже, він добре сформований ітерабельний об'єкт +[...aGeneratorObject]; +// [1, 2, 3] +</pre> + +<h2 id="Специфікації">Специфікації</h2> + +<table class="standard-table"> + <tbody> + <tr> + <th scope="col">Специфікація</th> + <th scope="col">Статус</th> + <th scope="col">Коментар</th> + </tr> + <tr> + <td>{{SpecName('ES2015', '#sec-iteration', 'Iteration')}}</td> + <td>{{Spec2('ES2015')}}</td> + <td>Початкова виознака.</td> + </tr> + <tr> + <td>{{SpecName('ESDraft', '#sec-iteration', 'Iteration')}}</td> + <td>{{Spec2('ESDraft')}}</td> + <td></td> + </tr> + </tbody> +</table> + +<h2 id="Див._також">Див. також</h2> + +<ul> + <li>Більше інформації щодо генераторів ES2015 дивіться у <a href="/uk/docs/Web/JavaScript/Reference/Statements/function*">документації по function*</a>.</li> +</ul> |