aboutsummaryrefslogtreecommitdiff
path: root/files/vi/web/javascript/reference/iteration_protocols/index.html
blob: 24853823497987b3af685918e9425761dc2a739e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
---
title: Iteration protocols
slug: Web/JavaScript/Reference/Iteration_protocols
translation_of: Web/JavaScript/Reference/Iteration_protocols
---
<div>{{jsSidebar("More")}}</div>

<p>Là một vài bổ sung cho ECMAScript 2015, <strong>Iteration protocols</strong> không phải là một tích hợp sẵn hay cú pháp mới mà là <em>protocols</em>. Các giao thức này có thể triển khai bởi bất kỳ đối tượng nào đơn giản bằng cách thực hiện theo một số quy ước.</p>

<p>Có 2 giao thức: <a href="#The_iterable_protocol">iterable protocol</a> và <a href="#The_iterator_protocol">iterator protocol</a>.</p>

<h2 id="The_iterable_protocol">The iterable protocol</h2>

<p><strong>Iterable protocol</strong> cho phép các đối tượng của JavaScript xác định hoặc tuỳ chỉnh hành vi lặp của chúng, chẳng hạn như giá trị nào sẽ được lặp trong vòng lặp {{jsxref("Statements/for...of", "for...of")}}. Một số kiểu tích hợp sẵn <a href="#Built-in_iterables">built-in iterables</a> là hành vi lặp mặc định, chẳng hạn như {{jsxref("Array")}} hoặc {{jsxref("Map")}}, trong khi các kiểu khác (chẳng hạn {{jsxref("Object")}}) không có.</p>

<p>Để có thể triển khai giao thức <strong>iterable</strong>, một đối tượng phải có phương thức <strong><code>@@iterator</code></strong>, điều này có nghĩa là một đối tượng (hoặc một trong các đối tượng trên <a href="/en-US/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain">prototype chain</a> của đối tượng đó) phải có một thuộc tính <code>@@iterator</code> và thao tác với thuộc tính đó thông qua hằng số {{jsxref("Symbol.iterator")}}:</p>

<table class="standard-table">
 <thead>
  <tr>
   <th scope="col">Property</th>
   <th scope="col">Value</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td><code>[Symbol.iterator]</code></td>
   <td>Một hàm không có tham số đầu vào trả ra một đối tượng phù hợp với <a href="#The_iterator_protocol">iterator protocol</a>.</td>
  </tr>
 </tbody>
</table>

<p>Bất cứ khi nào một đối tượng thực hiện vòng lặp (chẳng hạn như sử dụng vòng lặp {{jsxref("Statements/for...of", "for...of")}}), phương thức <code>@@iterator</code> sẽ được gọi mà không có tham số đầu vào, và trả ra <strong>iterator</strong> được sử dụng để thu được giá trị được lặp.</p>

<p>Lưu ý khi hàm không tham số đầu vào được gọi, nó sẽ được gọi như là một phương thức của iterable object. Do đó bên trong hàm, từ khoá <code>this</code> có thể được sử dụng để truy cập vào các thuộc tính của iterable object, để quyết định những gì được cung cấp trong quá trình lặp.</p>

<p>Hàm này có thể là một hàm bình thường hoặc nó có thể là một generator function, do đó khi được gọi, một iterator object sẽ được trả về. Bên trong của generator function, mỗi giá trị trả về có thể cung cấp bằng cách sử dụng <code>yield</code>.</p>

<h2 id="The_iterator_protocol">The iterator protocol</h2>

<p><strong>Ierator protocol</strong> định nghĩa một cách tiêu chuẩn để tạo ra một chuỗi các giá trị (hưu hạn hoặc vô hạn), và  có thể trả về 1 giá trị khi tất cả các giá trị đã được tạo.</p>

<p>Một đối tượng là một iterator khi nó triển khai phương thức <code><strong>next()</strong></code> với ý nghĩa như sau:</p>

<table class="standard-table">
 <thead>
  <tr>
   <th scope="col">Property</th>
   <th scope="col">Value</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td><code>next()</code></td>
   <td>
    <p>Một hàm không tham số đầu vào trả ra một đối tượng có ít nhất 2 thuộc tính sau: </p>

    <dl>
     <dt><code>done</code> (boolean)</dt>
     <dd>
     <p>Có giá trị <code>false</code> nếu iterator có thể tạo ra giá trị tiếp theo trong chuỗi. (This is equivalent to not specifying the <code>done</code> property altogether.)</p>

     <p>Có giá trị <code>true</code> nếu iterator kết thúc chuỗi. Trong trường hợp này, <code>value</code> có thể tuỳ chọn giá trị trả về cho iterator.</p>
     </dd>
     <dt><code>value</code></dt>
     <dd>Bất kỳ giá trị JavaScript nào được trả về bởi iterator. ?Có thể bỏ qua khi <code>done</code> là <code>true</code>.</dd>
    </dl>

    <p>Phương thức <code>next()</code> phải luôn luôn trả về một đối tượng với các thuộc tính thích hợp bao gồm <code>done</code> và <code>value</code>. Nếu một giá trị không phải đối tượng được trả về (chẳng hạn như <code>false</code> hoặc <code>undefined</code>), một {{jsxref("TypeError")}} (<code>"iterator.next() returned a non-object value"</code>) sẽ được đẩy ra.</p>
   </td>
  </tr>
 </tbody>
</table>

<div class="note">
<p><strong>Lưu ý:</strong> Không thể biết liệu một đối tượng cụ thể có triển khai giao thức iterator hay không. Tuy nhiên, dễ dàng để tạo ra một đối tượng mà có cả 2 giao thức iterator và iterable (như ví dụ dưới đây).</p>

<p>Làm như vậy cho phép một iterator có thể sử dụng các cú pháp đa dạng của iterables. Vì vậy, rất hiếm khi triển khai giao thức Iterator Protocol mà không triển khai Iterable.</p>

<pre class="brush: js example-good">// Satisfies both the Iterator Protocol and Iterable
let myIterator = {
    next: function() {
        // ...
    },
    [Symbol.iterator]: function() { return this; }
};
</pre>
</div>

<h2 id="Examples_using_the_iteration_protocols">Examples using the iteration protocols</h2>

<p>{{jsxref("String")}} là một ví dụ tích hợp sẵn iterable object:</p>

<pre class="brush: js">let someString = 'hi';
console.log(typeof someString[Symbol.iterator]); // "function"
</pre>

<p>{{jsxref("String/@@iterator", "iterator mặc định", "", 1)}} của <code>String</code>  trả ra lần lượt từng mã của các ký tự:</p>

<pre class="brush: js">let iterator = someString[Symbol.iterator]();
console.log(iterator + ''); // "[object String Iterator]"

console.log(iterator.next()); // { value: "h", done: false }
console.log(iterator.next()); // { value: "i", done: false }
console.log(iterator.next()); // { value: undefined, done: true }</pre>

<p>Some built-in constructs—such as the {{jsxref("Operators/Spread_operator", "spread syntax", "", 1)}}—use the same iteration protocol under the hood:</p>

<pre class="brush: js">console.log([...someString]); // ["h", "i"]</pre>

<p>You can redefine the iteration behavior by supplying our own <code>@@iterator</code>:</p>

<pre class="brush: js">// need to construct a String object explicitly to avoid auto-boxing
let someString = new String('hi');

someString[Symbol.iterator] = function () {
  return {
    // this is the iterator object, returning a single element (the string "bye")
    next: function () {
      return this._first ? {
        value: 'bye',
        done: (this._first = false)
      } : {
        done: true
      }
    },
    _first: true
  };
};
</pre>

<p>Notice how redefining <code>@@iterator</code> affects the behavior of built-in constructs that use the iteration protocol:</p>

<pre class="brush: js">console.log([...someString]); // ["bye"]
console.log(someString + ''); // "hi"
</pre>

<h2 id="Iterable_examples">Iterable examples</h2>

<h3 id="Built-in_iterables">Built-in iterables</h3>

<p>{{jsxref("String")}}, {{jsxref("Array")}}, {{jsxref("TypedArray")}}, {{jsxref("Map")}}, and {{jsxref("Set")}} are all built-in iterables, because each of their prototype objects implements an <code>@@iterator</code> method.</p>

<h3 id="User-defined_iterables">User-defined iterables</h3>

<p>You can make your own iterables like this:</p>

<pre class="brush: js">let myIterable = {};
myIterable[Symbol.iterator] = function* () {
    yield 1;
    yield 2;
    yield 3;
};
console.log([...myIterable]); // [1, 2, 3]
</pre>

<h3 id="Built-in_APIs_accepting_iterables">Built-in APIs accepting iterables</h3>

<p>There are many APIs that accept iterables. Some examples include:</p>

<ul>
 <li>{{jsxref("Map", "new Map([<var>iterable</var>])")}}</li>
 <li>{{jsxref("WeakMap", "new WeakMap([<var>iterable</var>])")}}</li>
 <li>{{jsxref("Set", "new Set([<var>iterable</var>])")}}</li>
 <li>{{jsxref("WeakSet", "new WeakSet([<var>iterable</var>])")}}</li>
</ul>

<pre class="brush: js">new Map([[1, 'a'], [2, 'b'], [3, 'c']]).get(2); // "b"

let myObj = {};

new WeakMap([
    [{}, 'a'],
    [myObj, 'b'],
    [{}, 'c']
]).get(myObj);             // "b"

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>

<h4 id="See_Also">See Also</h4>

<ul>
 <li>{{jsxref("Promise.all()", "Promise.all(<var>iterable</var>)")}}</li>
 <li>{{jsxref("Promise.race()", "Promise.race(<var>iterable</var>)")}}</li>
 <li>{{jsxref("Array.from()", "Array.from(<var>iterable</var>)")}}</li>
</ul>

<h3 id="Syntaxes_expecting_iterables">Syntaxes expecting iterables</h3>

<p>Some statements and expressions expect iterables, for example the {{jsxref("Statements/for...of", "for...of")}} loops, the {{jsxref("Operators/Spread_syntax", "spread operator", "", 1)}})}}, {{jsxref("Operators/yield*", "yield*")}}, and {{jsxref("Operators/Destructuring_assignment", "destructuring assignment")}}:</p>

<pre class="brush: js">for (let value of ['a', 'b', 'c']) {
    console.log(value);
}
// "a"
// "b"
// "c"

console.log([...'abc']);   // ["a", "b", "c"]

function* gen() {
  yield* ['a', 'b', 'c'];
}

console.log(gen().next()); // { value: "a", done: false }

[a, b, c] = new Set(['a', 'b', 'c']);
console.log(a);            // "a"

</pre>

<h3 id="Non-well-formed_iterables">Non-well-formed iterables</h3>

<p>If an iterable's <code>@@iterator</code> method doesn't return an iterator object, then it's considered a <em>non-well-formed</em> iterable.</p>

<p>Using one is likely to result in runtime errors or buggy behavior:</p>

<pre class="brush: js example-bad">let nonWellFormedIterable = {};
nonWellFormedIterable[Symbol.iterator] = () =&gt; 1;
[...nonWellFormedIterable]; // TypeError: [] is not a function
</pre>

<h2 id="Iterator_examples">Iterator examples</h2>

<h3 id="Simple_iterator">Simple iterator</h3>

<pre class="brush: js">function makeIterator(array) {
  let nextIndex = 0
  return {
    next: function() {
      return nextIndex &lt; array.length ? {
        value: array[nextIndex++],
        done: false
      } : {
        done: true
      };
    }
  };
}

let 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="Infinite_iterator">Infinite iterator</h3>

<pre class="brush: js">function idMaker() {
  let index = 0;
  return {
    next: function() {
      return {
        value: index++,
        done: false
      };
    }
  };
}

let it = idMaker();

console.log(it.next().value); // '0'
console.log(it.next().value); // '1'
console.log(it.next().value); // '2'
// ...
</pre>

<h3 id="With_a_generator">With a generator</h3>

<pre class="brush: js">function* makeSimpleGenerator(array) {
  let nextIndex = 0;
  while (nextIndex &lt; array.length) {
    yield array[nextIndex++];
  }
}

let gen = makeSimpleGenerator(['yo', 'ya']);

console.log(gen.next().value); // 'yo'
console.log(gen.next().value); // 'ya'
console.log(gen.next().done);  // true

function* idMaker() {
  let index = 0;
  while (true) {
    yield index++;
  }
}

let gen = idMaker()

console.log(gen.next().value); // '0'
console.log(gen.next().value); // '1'
console.log(gen.next().value); // '2'
// ...
</pre>

<h3 id="With_ES2015_class">With ES2015 class</h3>

<pre class="brush: js">class SimpleClass {
  constructor(data) {
    this.data = data;
  }

  [Symbol.iterator]() {
    // Use a new index for each iterator. This makes multiple
    // iterations over the iterable safe for non-trivial cases,
    // such as use of break or nested looping over the same iterable.
    let index = 0;

    return {
      next: () =&gt; {
        if (index &lt; this.data.length) {
          return {value: this.data[index++], done: false}
        } else {
          return {done: true}
        }
      }
    }
  }
}

const simple = new SimpleClass([1,2,3,4,5]);

for (const val of simple) {
  console.log(val); // '1' '2' '3' '4' '5'
}
</pre>

<h2 id="Is_a_generator_object_an_iterator_or_an_iterable">Is a generator object an iterator or an iterable?</h2>

<p>A {{jsxref("Generator", "generator object", "", 1)}} is <em>both</em> iterator and iterable:</p>

<pre class="brush: js">let aGeneratorObject = function* () {
  yield 1;
  yield 2;
  yield 3;
}();

console.log(typeof aGeneratorObject.next);
// "function", because it has a next method, so it's an iterator

console.log(typeof aGeneratorObject[Symbol.iterator]);
// "function", because it has an @@iterator method, so it's an iterable

console.log(aGeneratorObject[Symbol.iterator]() === aGeneratorObject);
// true, because its @@iterator method returns itself (an iterator), so it's an well-formed iterable

console.log([...aGeneratorObject]);
// [1, 2, 3]
</pre>

<h2 id="Specifications">Specifications</h2>

<table class="standard-table">
 <thead>
  <tr>
   <th scope="col">Specification</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td>{{SpecName('ESDraft', '#sec-iteration', 'Iteration')}}</td>
  </tr>
 </tbody>
</table>

<h2 id="See_also">See also</h2>

<ul>
 <li>To learn more about ES2015 generators, see:<br>
  {{jsxref("Statements/function*", "the <code>function*</code> documentation", "", 1)}}</li>
</ul>