<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          從理解到實(shí)現(xiàn)輕松掌握 ES6 中的迭代器

          共 7845字,需瀏覽 16分鐘

           ·

          2021-03-15 09:18


          今日文章由 “Nodejs技術(shù)棧@五月君” 授權(quán)分享,正文從下面開始~

          JavaScript 中除了 Array 之外,ES6 還新增加了 Map、Set 結(jié)構(gòu),當(dāng)我們需要操作這些數(shù)據(jù)時,就需要一種統(tǒng)一的接口來處理這些不同的數(shù)據(jù)結(jié)構(gòu)。ES6 中新增加的 Iterator(迭代器)就提供了這樣一種機(jī)制。

          Symbol.iterator 支持的數(shù)據(jù)結(jié)構(gòu)

          ES6 中提供了 Symbol.iterator 方法,該方法返回一個迭代器對象,目前 Array、Set、Map 這些數(shù)據(jù)結(jié)構(gòu)默認(rèn)具有 Symbol.iterator 屬性,如下所示,可以看到 Object 類型是沒有的。

          console.log([][Symbol.iterator]()); // Object [Array Iterator] {}
          console.log((new Map())[Symbol.iterator]()); // [Map Entries] {  }
          console.log((new Set())[Symbol.iterator]()); // [Set Iterator] {  }
          console.log({}[Symbol.iterator]); // undefined

          除了上面提到這些數(shù)據(jù)結(jié)構(gòu),JavaScript 中一些類似數(shù)組的對象也默認(rèn)具有 Symbol.iterator 屬性,例如:字符串、arguments 對象、DOM 的 NodeList 對象。

          • 字符串
          const str = 'nodejs';
          console.log(str[Symbol.iterator]()); // Object [String Iterator] {}

          for (const val of str) {
            console.log(val); // n o d e j s
          }
          • arguments 對象
          function print({
            console.log(arguments[Symbol.iterator]()); // Object [Array Iterator] {}
            for (const val of arguments) {
              console.log(val); // n o d e
            }
          }
          print('n''o''d''e')
          • DOM NodeList 對象
          const divNodeList = document.getElementsByTagName('div')
          console.log(divNodeList[Symbol.iterator]()) // Array Iterator {}
          for (const div of divNodeList) {
           // 會輸出每個 div 標(biāo)簽
           console.log(div);
          }

          迭代器對象的 next 方法

          調(diào)用可迭代對象的 Symbol.iterator 方法會返回一個迭代器對象,它的接口中有一個 next 方法,該方法返回 value 和 done 兩個屬性,其中 value 屬性是當(dāng)前成員的值,done 屬性表示遍歷是否結(jié)束。了解生成器函數(shù)(Generator)的可能不會陌生,同樣的當(dāng)你執(zhí)行一個生成器函數(shù)也會得到一個迭代器對象,但是要區(qū)分 生成器和迭代器不是一個概念。

          const arr = ['N''o''d''e'];
          const iterator = arr[Symbol.iterator]();

          console.log(iterator.next()); // { value: 'N', done: false }
          console.log(iterator.next()); // { value: 'o', done: false }
          console.log(iterator.next()); // { value: 'd', done: false }
          console.log(iterator.next()); // { value: 'e', done: false }
          console.log(iterator.next()); // { value: undefined, done: true }

          上例中聲明一個數(shù)組 arr,調(diào)用 arr 的 Symbol.iterator 方法創(chuàng)建了一個迭代器對象 iterator 之后不斷調(diào)用 next 方法返回當(dāng)前數(shù)組內(nèi)容,直到 next 方法返回值 done 為 true 則該數(shù)組訪問完畢。

          Iterator 接口遍歷

          解構(gòu)賦值

          數(shù)組、Set、Map 解構(gòu)賦值時,會默認(rèn)調(diào)用 Symbol.iterator 方法。注意 Map 調(diào)用 Symbol.iterator 方法返回的是一個 entries 方法,該方法返回的是一個新的迭代器對象且按插入順序包含了 Map 對象中每個元素的 [key, value] 數(shù)組,所以調(diào)用 Map 實(shí)例的 keys 或 values 方法也會返回一個新的迭代器對象。

          const set = new Set().add('n').add('o');
          const map = new Map().set('d').set('e');
          const [xSet, ySet] = set;
          console.log(xSet, ySet) // n o
          const [xMap, yMap] = map.keys();
          console.log(xMap, yMap) // d e

          擴(kuò)展運(yùn)算符

          ES6 中的擴(kuò)展運(yùn)算符(...)也會默認(rèn)調(diào)用數(shù)組、Set、Map 等結(jié)構(gòu)的 Symbol.iterator 方法。

          const set = new Set('node');
          const [x, y, ...z] = set;
          console.log(x, y, z); // n o [ 'd', 'e' ]

          for...of 循環(huán)

          ES6 借鑒了 C++、Python 等語言引入了 for...of 循環(huán),該循環(huán)內(nèi)部也會調(diào)用 Symbol.iterator 方法,只要具有 Iterator 接口的數(shù)據(jù)結(jié)構(gòu)都可以使用。

          const set = new Set().add('n').add('o');

          for (const val of set) {
            console.log(val);
          }

          for...of 循環(huán)在執(zhí)行中還可以使用 break; 中斷迭代器的執(zhí)行。以下示例,修改循環(huán)語句在執(zhí)行第一次 val 等于 n 之后執(zhí)行 break。

          for (const val of set) {
            console.log(val); // n
            if (val === 'n'break;
          }

          其它方法

          數(shù)組默認(rèn)是支持 Iterator 接口,所以任何接收數(shù)組做為參數(shù)的方法也都會默認(rèn)調(diào)用 Symbol.iterator 方法,如下所示:

          const set = new Set().add('n').add('o');
          console.log(Array.from(set)); // [ 'n', 'o' ]
          Promise.all(set).then(val => console.log(val)) // [ 'n', 'o' ]
          Promise.race(set).then(val => console.log(val)) // n

          自定義迭代器

          迭代協(xié)議

          • 參照可迭代協(xié)議,要成為可迭代對象首先要有一個 **@@iterator **即(Symbol.iterator)屬性,該屬性為一個無參數(shù)的函數(shù),返回一個符合迭代器協(xié)議的對象。
          • 根據(jù)迭代器協(xié)議定義這個迭代器對象要返回一個 next() 方法,這個 next() 方法返回一個包含 value、done 屬性的對象。
          const  myIterator = {
            // for...of 循環(huán)會用到
            [Symbol.iterator]: function(return this },
            
            // 標(biāo)準(zhǔn)的迭代器接口方法
            nextfunction({
             // ...
            }
          }

          如果用 TypeScript 寫法描述如下:

          // 遍歷器接口 Iterable
          interface Iterable {
           [Symbol.iterator]: Iterator
          }

          // 迭代器對象
          interface Iterator {
           next(value?: any): IterationResult,
          }

          // next 方法返回值定義
          interface IterationResult {
           value: any,
            done: boolean
          }

          基于普通函數(shù)的迭代器實(shí)現(xiàn)

          迭代器的函數(shù)實(shí)現(xiàn)可以是一個普通函數(shù)也可以是一個生成器函數(shù),我們先以普通函數(shù)為例,定義一個 Range 構(gòu)造函數(shù),用來輸出兩個數(shù)值區(qū)域的所有值。

          function Range(start, end{
            this.id = start;
            this.end = end;
          }
          Range.prototype[Symbol.iterator] = function(return this }
          Range.prototype.next = function next({
            if (this.id > this.end) {
              return { value: undefined, done: true }
            }

            return { value: this.id++, done: false }
          }
          const r1 = new Range(03);
          const it = r1[Symbol.iterator]()
          for (const id of r1) {
            console.log(id); // 0,1,2,3
          }

          基于生成器函數(shù)的迭代器實(shí)現(xiàn)

          使用生成器函數(shù)(Generator)實(shí)現(xiàn)是最簡單的,只要使用 yield 語句返回每一次的值即可。如下所示:

          Range.prototype[Symbol.iterator] = function* ({
            while (this.id <= this.end) {
              yield this.id++;
            }
          }

          異步迭代器

          到目前為止我們上面講解的都是同步模式的迭代器,這個很好理解,因?yàn)槲覀兊臄?shù)據(jù)源本身也就是同步的,但是在 Node.js 中一次網(wǎng)絡(luò) I/O 請求或者一次文件 I/O 請求,它們都是基于事件是異步的,所以我們就不能像使用 Symbol.iterator 的方式來使用。ECMAScript 2018 標(biāo)準(zhǔn)中提供了 **Symbol.asyncIterator **屬性,這是一個異步迭代器,如果一個對象設(shè)置了該屬性,它就是異步可迭代對象,相應(yīng)的我們要使用 for await...of 循環(huán)遍歷數(shù)據(jù)。

          自定義異步迭代器

          function Range(start, end{
            this.id = start;
            this.end = end;
          }
          // 與上面不同,function 前我們增加了 async 關(guān)鍵字
          Range.prototype[Symbol.asyncIterator] = async function* ({
            while (this.id <= this.end) {
              yield this.id++;
            }
          }
          const r1 = new Range(03);
          console.log(r1[Symbol.asyncIterator]()); // Object [AsyncGenerator] {}
          for await (const id of r1) {
            console.log(id); // 0,1,2,3
          }

          與同步迭代器的不同

          • 同步迭代器返回的是一個常規(guī)的  { value, done } 對象,而異步迭代器返回的是一個包含  { value, done } 的 Promise 對象。
          • 同步可迭代協(xié)議具有 Symbol.iterator 屬性,異步可迭代協(xié)議具有 Symbol.asyncIterator 屬性。
          • 同步迭代器使用 for...of 循環(huán)遍歷,異步迭代器使用 for await...of 循環(huán)遍歷。

          異步迭代器的支持

          目前沒有默認(rèn)設(shè)定了 [Symbol.asyncIterator] 屬性的 JavaScript 內(nèi)建的對象。不過,WHATWG(網(wǎng)頁超文本應(yīng)用技術(shù)工作小組)Streams 會被設(shè)定為第一批異步可迭代對象,[Symbol.asyncIterator] 最近已在設(shè)計(jì)規(guī)范中落地。

          下一節(jié)我們將會講解異步迭代器在 Node.js 中的使用,歡迎關(guān)注。

          Reference

          [1]

          你不知道的JavaScript(中卷): https://book.douban.com/subject/26854244/

          [2]

          可迭代協(xié)議: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Iteration_protocols

          [3]

          Symbol.asyncIterator: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol/asyncIterator

          ??愛心三連擊

          1.看到這里了就點(diǎn)個在看支持下吧,你的點(diǎn)贊,在看是我創(chuàng)作的動力。

          2.關(guān)注公眾號程序員成長指北,回復(fù)「1」加入高級前端交流群!「在這里有好多 前端 開發(fā)者,會討論 前端 Node 知識,互相學(xué)習(xí)」!

          3.也可添加微信【ikoala520】,一起成長。


          “在看轉(zhuǎn)發(fā)”是最大的支持

          瀏覽 28
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲中文无码乱伦 | 俺来了俺去了www色官网 | 国产性爱无码 | 日本免费版网站nba | 高清无码在线免费视频 |