diff options
Diffstat (limited to 'files/vi/web/javascript/reference/iteration_protocols/index.html')
-rw-r--r-- | files/vi/web/javascript/reference/iteration_protocols/index.html | 383 |
1 files changed, 383 insertions, 0 deletions
diff --git a/files/vi/web/javascript/reference/iteration_protocols/index.html b/files/vi/web/javascript/reference/iteration_protocols/index.html new file mode 100644 index 0000000000..2485382349 --- /dev/null +++ b/files/vi/web/javascript/reference/iteration_protocols/index.html @@ -0,0 +1,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] = () => 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 < 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 < 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: () => { + if (index < 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> |