From daa1a2aff136fa9da1fcc97d7da97a2036fabc77 Mon Sep 17 00:00:00 2001 From: Florian Merz Date: Thu, 11 Feb 2021 14:51:47 +0100 Subject: unslug uk: move --- .../reference/iteration_protocols/index.html | 352 +++++++++++++++++++++ 1 file changed, 352 insertions(+) create mode 100644 files/uk/web/javascript/reference/iteration_protocols/index.html (limited to 'files/uk/web/javascript/reference/iteration_protocols') 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..d5899043ca --- /dev/null +++ b/files/uk/web/javascript/reference/iteration_protocols/index.html @@ -0,0 +1,352 @@ +--- +title: Протоколи перебору +slug: Web/JavaScript/Reference/Протоколи_перебору +tags: + - ECMAScript 2015 + - JavaScript + - Ітератор + - ітерабельний об'єкт +translation_of: Web/JavaScript/Reference/Iteration_protocols +--- +
{{jsSidebar("More")}}
+ +

Пара доповнень до ECMAScript 2015 є не новими вбудованими елементами чи синтаксисом, а протоколами. Ці протоколи можуть реалізовуватись будь-яким об'єктом, що відповідає певним правилам.

+ +

Існують два протоколи: протокол ітерабельного об'єкта і протокол ітератора.

+ +

Протокол ітерабельного об'єкта

+ +

Протокол ітерабельного об'єкта дозволяє об'єктам JavaScript визначати чи налаштовувати свою ітераційну поведінку, наприклад, через які значення буде проходити цикл у конструкції {{jsxref("Statements/for...of", "for..of")}}. Деякі вбудовані типи є вбудованими ітерабельними об'єктами з визначеною за замовчуванням ітераційною поведінкою, наприклад, {{jsxref("Array")}} або {{jsxref("Map")}}, в той час, як інші типи (такі, як {{jsxref("Object")}}) не є ітерабельними.

+ +

Для того, щоб бути ітерабельним, об'єкт має реалізувати метод @@iterator, тобто, цей об'єкт (або один з об'єктів у його ланцюжку прототипів) повинен мати властивість з ключем @@iterator, доступну через константу {{jsxref("Symbol.iterator")}}:

+ + + + + + + + + + + + + + +
ВластивістьЗначення
[Symbol.iterator]Функція без аргументів, яка повертає об'єкт, що відповідає протоколу ітератора.
+ +

Коли виникає необхідність перебрати об'єкт (наприклад, на початку циклу for..of), його метод @@iterator викликається без аргументів, а ітератор, який він повертає, використовується для отримання значень, що перебираються.

+ +

Протокол ітератора

+ +

Протокол ітератора визначає стандартний спосіб створювати послідовності значень (скінченні або нескінченні).

+ +

Об'єкт є ітератором, коли реалізує метод next() з наступною семантикою:

+ + + + + + + + + + + + +
ВластивістьЗначення
next +

Функція з нулем аргументів, яка повертає об'єкт з двома властивостями:

+ +
    +
  • done (булеве значення) + +
      +
    • Має значення true, якщо ітератор досяг кінця послідовності, що перебирається. В цьому випадку value може містити значення, що повертається ітератором. Значення, що повертаються, пояснюються тут.
    • +
    • Має значення false, якщо ітератор був здатний надати наступне значення послідовності. Це аналогічно тому, щоб взагалі не вказувати значення властивості done.
    • +
    +
  • +
  • value - будь-яке значення JavaScript, що повертає ітератор. Його можна не вказувати, коли done дорівнює true.
  • +
+ +

Метод next завжди повинен повертати об'єкт з належними властивостями, в тому числі done та value. Якщо повертається значення, що не є об'єктом (наприклад, false чи undefined), буде викинуто помилку {{jsxref("TypeError")}} ("iterator.next() returned a non-object value").

+
+ +
+

Неможливо знати, чи певний об'єкт реалізує протокол ітератора, однак, можна легко створити об'єкт, який відповідає обом протоколам, ітератора та ітерабельного об'єкта (як показано нижче у прикладі). Це дозволяє використовувати ітератор там, де очікується ітерабельний об'єкт. Тому нечасто є потреба реалізовувати протокол ітератора, не реалізуючи також протокол ітерабельного об'єкта. 

+ +
var myIterator = {
+    next: function() {
+        // ...
+    },
+    [Symbol.iterator]: function() { return this }
+};
+
+
+ +

Приклади застосування протоколів перебору

+ +

Об'єкт {{jsxref("String")}} є прикладом вбудованого ітерабельного об'єкта:

+ +
var someString = '13';
+typeof someString[Symbol.iterator];          // "function"
+
+ +

Вбудований ітератор об'єкта String повертає коди символів рядка один за одним:

+ +
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 }
+ +

Деякі вбудовані конструкції, такі як оператор розпакування, використовують під капотом той самий протокол перебору:

+ +
[...someString]                              // ["1", "3"]
+ +

Ми можемо перевизначити поведінку під час перебору, надавши свій власний метод @@iterator:

+ +
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
+  };
+};
+
+ +

Зверніть увагу, як перевизначення методу @@iterator впливає на поведінку вбудованих конструкцій, що використовують протокол перебору:

+ +
[...someString];                             // ["бувай"]
+someString + '';                             // "привіт"
+
+ +

Приклади ітерабельних об'єктів

+ +

Вбудовані ітерабельні об'єкти

+ +

{{jsxref("String")}}, {{jsxref("Array")}}, {{jsxref("TypedArray")}}, {{jsxref("Map")}} та {{jsxref("Set")}} всі є вбудованими ітерабельними об'єктами, тому що кожний з їхніх прототипів реалізує метод @@iterator.

+ +

Створені користувачем ітерабельні об'єкти

+ +

Ми можемо створювати власні ітерабельні об'єкти наступним чином:

+ +
var myIterable = {};
+myIterable[Symbol.iterator] = function* () {
+    yield 1;
+    yield 2;
+    yield 3;
+};
+[...myIterable]; // [1, 2, 3]
+
+ +

Вбудовані API, що приймають ітерабельні об'єкти

+ +

Існує багато API, які приймають ітерабельні об'єкти, наприклад: {{jsxref("Map", "Map([iterable])")}}, {{jsxref("WeakMap", "WeakMap([iterable])")}}, {{jsxref("Set", "Set([iterable])")}} and {{jsxref("WeakSet", "WeakSet([iterable])")}}:

+ +
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
+
+ +

Дивіться також {{jsxref("Promise.all", "Promise.all(iterable)")}}, {{jsxref("Promise.race", "Promise.race(iterable)")}} та {{jsxref("Array.from", "Array.from()")}}.

+ +

Синтаксис, що очікує на ітерабельний об'єкт

+ +

Деякі оператори та вирази очікують на ітерабельні об'єкти, наприклад, цикли for-of, оператор розпакування, yield* та деструктуризаційне присвоєння:

+ +
for(let value of ['а', 'б', 'в']){
+    console.log(value);
+}
+// "а"
+// "б"
+// "в"
+
+[...'абв']; // ["а", "б", "в"]
+
+function* gen() {
+  yield* ['а', 'б', 'в'];
+}
+
+gen().next(); // { value:"а", done:false }
+
+[a, b, c] = new Set(['а', 'б', 'в']);
+a // "а"
+
+
+ +

Погано сформовані ітерабельні об'єкти

+ +

Якщо метод ітерабельного об'єкта @@iterator не повертає об'єкт ітератора, то це погано сформований ітерабельний об'єкт. Використання його в такому вигляді ймовірно призведе до викидання винятків під час виконання або помилкової поведінки:

+ +
var nonWellFormedIterable = {}
+nonWellFormedIterable[Symbol.iterator] = () => 1
+[...nonWellFormedIterable] // TypeError: [] is not a function
+
+ +

Приклади ітераторів

+ +

Простий ітератор

+ +
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
+
+ +

Нескінченний ітератор

+ +
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'
+// ...
+
+ +

З генератором

+ +
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'
+// ...
+
+ +

З класом ES2015

+ +
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'
+}
+
+ +

Генератор є ітератором чи ітерабельним об'єктом?

+ +

Об'єкт генератор є одночасно ітератором та ітерабельним об'єктом:

+ +
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]
+
+ +

Специфікації

+ + + + + + + + + + + + + + + + + + + +
СпецифікаціяСтатусКоментар
{{SpecName('ES2015', '#sec-iteration', 'Iteration')}}{{Spec2('ES2015')}}Початкова виознака.
{{SpecName('ESDraft', '#sec-iteration', 'Iteration')}}{{Spec2('ESDraft')}}
+ +

Див. також

+ + -- cgit v1.2.3-54-g00ecf