diff options
author | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:40:17 -0500 |
---|---|---|
committer | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:40:17 -0500 |
commit | 33058f2b292b3a581333bdfb21b8f671898c5060 (patch) | |
tree | 51c3e392513ec574331b2d3f85c394445ea803c6 /files/zh-cn/web/javascript/reference/iteration_protocols/index.html | |
parent | 8b66d724f7caf0157093fb09cfec8fbd0c6ad50a (diff) | |
download | translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.tar.gz translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.tar.bz2 translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.zip |
initial commit
Diffstat (limited to 'files/zh-cn/web/javascript/reference/iteration_protocols/index.html')
-rw-r--r-- | files/zh-cn/web/javascript/reference/iteration_protocols/index.html | 390 |
1 files changed, 390 insertions, 0 deletions
diff --git a/files/zh-cn/web/javascript/reference/iteration_protocols/index.html b/files/zh-cn/web/javascript/reference/iteration_protocols/index.html new file mode 100644 index 0000000000..8f124ba39b --- /dev/null +++ b/files/zh-cn/web/javascript/reference/iteration_protocols/index.html @@ -0,0 +1,390 @@ +--- +title: 迭代协议 +slug: Web/JavaScript/Reference/Iteration_protocols +tags: + - ECMAScript 2015 + - JavaScript + - 可迭代协议 + - 迭代 + - 迭代器 + - 迭代器协议 +translation_of: Web/JavaScript/Reference/Iteration_protocols +--- +<div>{{jsSidebar("More")}}</div> + +<p>作为 ECMAScript 2015 的一组补充规范,迭代协议并不是新的内置实现或语法,而是<em>协议</em>。这些协议可以被任何遵循某些约定的对象来实现。</p> + +<p>迭代协议具体分为两个协议:<a href="#可迭代协议">可迭代协议</a>和<a href="#迭代器协议">迭代器协议</a>。</p> + +<h2 id="可迭代协议">可迭代协议</h2> + +<p><strong>可迭代协议</strong>允许 JavaScript 对象定义或定制它们的迭代行为,例如,在一个 {{jsxref("Statements/for...of", "for..of")}} 结构中,哪些值可以被遍历到。一些内置类型同时是<a href="#内置可迭代对象">内置可迭代对象</a>,并且有默认的迭代行为,比如 {{jsxref("Array")}} 或者 {{jsxref("Map")}},而其他内置类型则不是(比如 {{jsxref("Object")}}))。</p> + +<p>要成为<strong>可迭代</strong>对象, 一个对象必须实现 <code><strong>@@iterator</strong></code> 方法。这意味着对象(或者它<a href="/zh-CN/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain">原型链</a>上的某个对象)必须有一个键为 <code>@@iterator</code> 的属性,可通过常量 {{jsxref("Symbol.iterator")}} 访问该属性:</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> + <p>一个无参数的函数,其返回值为一个符合<a href="#迭代器协议">迭代器协议</a>的对象。</p> + </td> + </tr> + </tbody> +</table> + +<p>当一个对象需要被迭代的时候(比如被置入一个 {{jsxref("Statements/for...of", "for...of")}} 循环时),首先,会不带参数调用它的 <code>@@iterator</code> 方法,然后使用此方法返回的<strong>迭代器</strong>获得要迭代的值。</p> + +<p>值得注意的是调用此零个参数函数时,它将作为对可迭代对象的方法进行调用。 因此,在函数内部,<code>this</code>关键字可用于访问可迭代对象的属性,以决定在迭代过程中提供什么。</p> + +<p>此函数可以是普通函数,也可以是生成器函数,以便在调用时返回迭代器对象。 在此生成器函数的内部,可以使用<code>yield</code>提供每个条目。</p> + +<h2 id="迭代器协议"><strong>迭代器协议</strong></h2> + +<p><strong>迭代器协议</strong>定义了产生一系列值(无论是有限个还是无限个)的标准方式。当值为有限个时,所有的值都被迭代完毕后,则会返回一个默认返回值。</p> + +<p>只有实现了一个拥有以下语义(semantic)的 <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> + + <dl> + <dt><code>done</code>(boolean)</dt> + <dd> + <p>如果迭代器可以产生序列中的下一个值,则为 <code>false</code>。(这等价于没有指定 <code>done</code> 这个属性。)</p> + + <p>如果迭代器已将序列迭代完毕,则为 <code>true</code>。这种情况下,<code>value</code> 是可选的,如果它依然存在,即为迭代结束之后的默认返回值。</p> + </dd> + <dt><code>value</code></dt> + <dd>迭代器返回的任何 JavaScript 值。done 为 true 时可省略。</dd> + </dl> + + <p><code>next()</code> 方法必须返回一个对象,该对象应当有两个属性: <code>done</code> 和 <code>value</code>,如果返回了一个非对象值(比如 <code>false</code> 或 <code>undefined</code>),则会抛出一个 {{jsxref("TypeError")}} 异常(<code>"iterator.next() returned a non-object value"</code>)。</p> + </td> + </tr> + </tbody> +</table> + +<div class="note"> +<p><strong>备注:</strong>不可能判断一个特定的对象是否实现了迭代器协议,然而,创造一个<em>同时</em>满足迭代器协议和可迭代协议的对象是很容易的(如下面的示例中所示)。</p> + +<p>这样做允许一个迭代器能被各种需要可迭代对象的语法所使用。因此,很少会只实现迭代器协议,而不实现可迭代协议。</p> + +<pre class="brush: js example-good notranslate"><code>var myIterator = { + next: function() { + // ... + }, + [Symbol.iterator]: function() { return this } +}</code></pre> +</div> + +<h2 id="使用迭代协议的例子">使用迭代协议的例子</h2> + +<p>{{jsxref("String")}} 是一个内置的可迭代对象:</p> + +<pre class="brush: js notranslate">let someString = "hi"; +typeof someString[Symbol.iterator]; // "function" +</pre> + +<p><code>String</code> 的默认迭代器会依次返回该字符串的各码点(code point):</p> + +<pre class="brush: js notranslate">let iterator = someString[Symbol.iterator](); +iterator + ""; // "[object String Iterator]" + +iterator.next(); // { value: "h", done: false } +iterator.next(); // { value: "i", done: false } +iterator.next(); // { value: undefined, done: true }</pre> + +<p>一些内置的语法结构——比如{{jsxref("Operators/Spread_operator", "展开语法")}}——其内部实现也使用了同样的迭代协议:</p> + +<pre class="brush: js notranslate">[...someString] // ["h", "i"]</pre> + +<p>我们可以通过提供自己的 <code>@@iterator</code> 方法,重新定义迭代行为:</p> + +<pre class="brush: js notranslate">// 必须构造 String 对象以避免字符串字面量 auto-boxing +var someString = new String("hi"); +someString[Symbol.iterator] = function() { + return { // 只返回一次元素,字符串 "bye",的迭代器对象 + next: function() { + if (this._first) { + this._first = false; + return { value: "bye", done: false }; + } else { + return { done: true }; + } + }, + _first: true + }; +}; +</pre> + +<p>注意重新定义的 <code>@@iterator</code> 方法是如何影响内置语法结构的行为的:</p> + +<pre class="brush: js notranslate">[...someString]; // ["bye"] +someString + ""; // "hi" +</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 notranslate">var myIterable = {}; +myIterable[Symbol.iterator] = function* () { + yield 1; + yield 2; + yield 3; +}; +[...myIterable]; // [1, 2, 3] +</pre> + +<h3 id="接受可迭代对象的内置_API">接受可迭代对象的内置 API</h3> + +<p>很多 API 接受可迭代对象作为参数,例如:</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="notranslate">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="另外,还有…">另外,还有…</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="需要可迭代对象的语法">需要可迭代对象的语法</h3> + +<p>一些语句和表达式需要可迭代对象,比如 {{jsxref("Statements/for...of", "for...of")}} 循环、{{jsxref("Operators/Spread_syntax", "展开语法")}}、{{jsxref("Operators/yield*", "yield*")}},和{{jsxref("Operators/Destructuring_assignment", "解构赋值")}}。</p> + +<pre class="brush: js notranslate">for(let value of ["a", "b", "c"]){ + console.log(value); +} +// "a" +// "b" +// "c" + +[..."abc"]; // ["a", "b", "c"] + +function* gen() { + yield* ["a", "b", "c"]; +} + +gen().next(); // { value: "a", done: false } + +[a, b, c] = new Set(["a", "b", "c"]); +a // "a" + +</pre> + +<h3 id="格式不佳的可迭代对象">格式不佳的可迭代对象</h3> + +<p>如果一个可迭代对象的 <code>@@iterator</code> 方法不能返回迭代器对象,那么可以认为它是一个<em>格式不佳</em>的(<em>Non-well-formed</em>)可迭代对象 。</p> + +<p>使用这样的可迭代对象很可能会导致如下的运行时(runtime)异常,或者不可预料的表现:</p> + +<pre class="brush: js example-bad notranslate">var nonWellFormedIterable = {} +nonWellFormedIterable[Symbol.iterator] = () => 1 +[...nonWellFormedIterable] // TypeError: [] is not a function +</pre> + +<h2 id="迭代器示例">迭代器示例</h2> + +<h3 id="简单迭代器">简单迭代器</h3> + +<pre class="brush: js notranslate">function makeIterator(array) { + let nextIndex = 0; + return { + next: function () { + return nextIndex < array.length ? { + value: array[nextIndex++], + done: false + } : { + done: true + }; + } + }; +} + +let it = makeIterator(['哟', '呀']); + +console.log(it.next().value); // '哟' +console.log(it.next().value); // '呀' +console.log(it.next().done); // true +</pre> + +<h3 id="无穷迭代器">无穷迭代器</h3> + +<pre class="brush: js notranslate">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="使用生成器">使用生成器</h3> + +<pre class="brush: js notranslate">function* makeSimpleGenerator(array) { + let nextIndex = 0; + + while(nextIndex < array.length) { + yield array[nextIndex++]; + } +} + +let gen = makeSimpleGenerator(['哟', '呀']); + +console.log(gen.next().value); // '哟' +console.log(gen.next().value); // '呀' +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="ES2015_类_class_中的迭代器">ES2015 类 class 中的迭代器</h3> + +<pre class="brush: js notranslate">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="生成器对象到底是一个迭代器,还是一个可迭代对象?">生成器对象到底是一个迭代器,还是一个可迭代对象?</h2> + +<p>{{jsxref("Generator", "生成器")}}对象既是迭代器,也是可迭代对象:</p> + +<pre class="brush: js notranslate">let 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] + +console.log(Symbol.iterator in aGeneratorObject) +// 返回true, 因为@@iterator方法是aGeneratorObject的一个属性</pre> + +<h2 id="规范">规范</h2> + +<table class="standard-table"> + <thead> + <tr> + <th scope="col">规范</th> + </tr> + </thead> + <tbody> + <tr> + <td>{{SpecName('ESDraft', '#sec-iteration', 'Iteration')}}</td> + </tr> + </tbody> +</table> + +<h2 id="参见">参见</h2> + +<ul> + <li>若想了解更多有关 ES2015 生成器的信息,请参考 {{jsxref("Statements/function*", "the <code>function*</code>")}} 的文档。</li> +</ul> |