JavaScript迭代与迭代器(Iterable and Iterator)

fu4723 7年前
   <p>我们在很前面的时候就讲到了迭代器这么一个东西。那么他究竟是什么呢?又有什么样的作用呢?本节我们就来讲述 Iterables 与 Iterators。也就是可迭代性与迭代器。</p>    <h2>概述</h2>    <p>ES6 中新增了一个迭代的接口,叫做可迭代性(Iterable )。本节呢,将要向大家讲述它是怎么工作的,以及它运用于那些 ECMAScript 语言类型中。</p>    <p>我们来看看迭代(Iteration )是什么,它分为两个部分:</p>    <ul>     <li><strong>Iterable:</strong> 可迭代性是一种数据结构,它希望使其元素可以访问公共部分。它通过内置系统的一个方法,键为 Symbol.iterator。这个方法就是迭代器的工厂。</li>     <li><strong>Iterator:</strong> 用于遍历数据结构的元素的指针。</li>    </ul>    <p>以下的值具有可迭代性:</p>    <ul>     <li>Array</li>     <li>String</li>     <li>Map</li>     <li>Set</li>     <li>DOM 数据结构(在程序中工作的那部分)</li>    </ul>    <p>那么具有可迭代性的对象可以做哪些事呢?如下所示:</p>    <p>(1)解构</p>    <pre>  <code class="language-javascript">let [a,b] = new Set(['a', 'b']);  console.log(a);  // "a"  console.log(b);  // "b"</code></pre>    <p>(2)for-of 循环</p>    <pre>  <code class="language-javascript">for (let x of ['a', 'b', 'c']) {      console.log(x);  }   // "a"  // "b"  // "c"</code></pre>    <p>(3)Array.from( ) 方法</p>    <pre>  <code class="language-javascript">let arr = Array.from(new Set(['a', 'b', 'c']));  //  ["a", "b", "c"]</code></pre>    <p>(4)展开操作符(...)</p>    <pre>  <code class="language-javascript">let arr = [...new Set(['a', 'b', 'c'])];   // ["a", "b", "c"]</code></pre>    <p>(5)Map 和 Set 构造函数</p>    <pre>  <code class="language-javascript">let map = new Map([      [1,'a'],      [2,'b']  ]);    let set = new Set(['a', 'b', 'c']);</code></pre>    <p>(6)Promise.all( ) 、Promise.race( ) 方法</p>    <pre>  <code class="language-javascript">Promise.all(iterableOverPromises).then(···);  Promise.race(iterableOverPromises).then(···);</code></pre>    <p>(7)yeild(*)</p>    <pre>  <code class="language-javascript">yield* anIterable;</code></pre>    <p>看完以上的这些,记住了,基本上这节内容就大致清楚了。</p>    <h2>可迭代性</h2>    <p>这里我们将要讲到可迭代性的思想,也就是说,可迭代到底是什么?</p>    <p>可迭代性的思想:</p>    <ul>     <li>数据消费者( <strong>Data consumers</strong> ):JavaScript 具有“消费”数据的语言结构。例如,for-of 循环用于遍历值,而扩展运算符(...)将值插入到数组或函数调用中。</li>     <li>数据源( <strong>Data Sources</strong> ):数据消费者可以从各种来源获取值。例如,您可能需要遍历数组的元素,或者Map 中的键值(使用 entries 方法)以及 String 实例中的字符等。</li>    </ul>    <p>我们这里必须清楚,想要每个数据消费者都能获取数据源,这是不切实际的。尤其是当我们创建新的源时(例如通过一个库)。因此,ES6 就提供了 Iterable 的接口。数据消费者使用它,而数据源负责实现它。我们来看下图:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/8cb678b3f63a6e209e2a6d52c6d446d3.png"></p>    <p>迭代关系</p>    <p>这里有两个知识:</p>    <ul>     <li>源( <strong>Source</strong> ):如果一个键拥有 Symbol.Iterator 方法并返回迭代器时,它的值就被认为是可迭代的。迭代器是一个对象,它可以使用 next( ) 方法返回值。我们可以这样说:这个方法每次使用时,都可以枚举值。</li>     <li>消耗( <strong>Consumption</strong> ):数据使用者使用迭代器来检索它们消耗的值。</li>    </ul>    <p>这里我们来举个之前的例子吧:</p>    <pre>  <code class="language-javascript">let set = new Set(['a','b','c']);  let keys = set.keys();  console.log(keys.next());  // {value: "a", done: false}  console.log(keys.next());  //  {value: "b", done: false}  console.log(keys.next());  // {value: "c", done: false}  console.log(keys.next());  // {value: undefined, done: true}</code></pre>    <p>上述示例中,我们使用了 keys() 方法与 next() 方法来进行该 Set 实例每个值的枚举与输出。就是因为 Set 拥有 Symbol.Iterator 方法。因此我们还可以使用下述的方式进行遍历:</p>    <pre>  <code class="language-javascript">let set = new Set(['a','b','c']);  let iter = set[Symbol.iterator]();  console.log(iter.next());  // {value: "a", done: false}  console.log(iter.next());  //  {value: "b", done: false}  console.log(iter.next());  // {value: "c", done: false}  console.log(iter.next());  // {value: undefined, done: true}</code></pre>    <p>输出结果一致,没有问题。</p>    <p>可以看到,next( ) 返回包装在对象中的每个项目,作为属性值的值。布尔属性 done 指示何时已达到项目序列的结束。</p>    <p>迭代和迭代器是所谓的协议(方法加上使用它们的规则)的一部分迭代。此协议的关键特性是它是顺序的:迭代器一次返回一个值。 这意味着如果一个可迭代的数据结构是非线性的(如一颗树),迭代将会使其线性化。</p>    <h2>迭代的数据源</h2>    <p>在概述中我举了几个例子。以下我使用 for-of 循环来迭代每个类型。我们一起来看下输出的值是什么:</p>    <p>(1)Array</p>    <pre>  <code class="language-javascript">for (let x of ['a', 'b']) {      console.log(x);  }   // "a"  // "b"</code></pre>    <p>(2)String</p>    <pre>  <code class="language-javascript">for (let x of 'Hello') {      console.log(x);  }  // "H"  // "e"  // "l"  // "l"  // "e"</code></pre>    <p>(3)Map</p>    <pre>  <code class="language-javascript">let map = new Map()      .set('a', 1)      .set('b', 2);  for (let pair of map) {      console.log(pair);  }  // ["a", 1]  // ["b", 2]</code></pre>    <p>(4)Set</p>    <pre>  <code class="language-javascript">let set = new Set()      .add('a')      .add('b');  for (let x of set) {      console.log(x);  }  // "a"  // "b"</code></pre>    <p>(5)arguments</p>    <pre>  <code class="language-javascript">function printArgs() {      for (let x of arguments) {          console.log(x);      }  }  printArgs('a', 'b');   // "a"  // "b"</code></pre>    <p>(6)DOM 数据结构</p>    <pre>  <code class="language-javascript">for (let node of document.querySelectorAll('div')) {      // ···  }</code></pre>    <p>现在更加清晰了吧?举出这么多例子的目的就是希望大家熟悉它。</p>    <h2>迭代计算的数据</h2>    <p>不是所有可迭代内容都必须来自数据结构,它也可以即时计算。</p>    <p>例如我们前面学习的 Array、Map、Set 都拥有 entries( )、keys( )、values( ) 三个方法。它们都是即时计算实例中的内容,再进行输出。</p>    <ul>     <li>entries( ) 方法返回实例的 [key, value] 的数组。</li>     <li>keys( ) 方法返回实例的键。</li>     <li>values( ) 方法返回实例的值。</li>    </ul>    <p>但是,我们需要知道,Object 类型是没有迭代性可言的。因此它没有 for-of 循环,只有 for-in 的遍历。当然,在未来可能会有内置方法。</p>    <pre>  <code class="language-javascript">// 错误  for (let x of {a: 1, b: 2}) { // TypeError      console.log(x);  }</code></pre>    <h2>总结</h2>    <p>本节把迭代的知识点讲完了,但是具体的用法还是比较多的。需要我们不断地实践。当你忘记的时候,就回来看看,将这些可迭代的对象牢记在脑海中,在使用时,就不会出错。</p>    <p> </p>    <p>来自:http://www.jianshu.com/p/2d0187f30a54</p>    <p> </p>