1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        可以迭代大部分數(shù)據(jù)類型的 for…of 為什么不能遍歷普通對象?

        共 15164字,需瀏覽 31分鐘

         ·

        2021-09-27 23:44


        for…of 及其使用

        我們知道,ES6 中引入 for...of 循環(huán),很多時候用以替代 for...in 和 forEach() ,并支持新的迭代協(xié)議。for...of 允許你遍歷 Array(數(shù)組), String(字符串), Map(映射), Set(集合),TypedArray(類型化數(shù)組)、arguments、NodeList對象、Generator等可迭代的數(shù)據(jù)結構等。for...of語句在可迭代對象上創(chuàng)建一個迭代循環(huán),調(diào)用自定義迭代鉤子,并為每個不同屬性的執(zhí)行語句。

        for...of的語法:

        for (variable of iterable) {
            // statement
        }
        // variable:每個迭代的屬性值被分配給該變量。
        // iterable:一個具有可枚舉屬性并且可以迭代的對象。

        常用用法

        {
          // 迭代字符串
          const iterable = 'ES6';
          for (const value of iterable) {
            console.log(value);
          }
          // Output:
          // "E"
          // "S"
          // "6"
        }
        {
          // 迭代數(shù)組
          const iterable = ['a''b'];
          for (const value of iterable) {
            console.log(value);
          }
          // Output:
          // a
          // b
        }
        {
          // 迭代Set(集合)
          const iterable = new Set([1221]);
          for (const value of iterable) {
            console.log(value);
          }
          // Output:
          // 1
          // 2
        }
        {
          // 迭代Map
          const iterable = new Map([["a"1], ["b"2], ["c"3]]);
          for (const entry of iterable) {
            console.log(entry);
          }
          // Output:
          // ["a", 1]
          // ["b", 2]
          // ["c", 3]

          for (const [key, value] of iterable) {
            console.log(value);
          }
          // Output:
          // 1
          // 2
          // 3
        }
        {
          // 迭代Arguments Object(參數(shù)對象)
          function args() {
            for (const arg of arguments) {
              console.log(arg);
            }
          }
          args('a''b');
          // Output:
          // a
          // b
        }
        {
          // 迭代生成器
          functionfoo()
            yield 1
            yield 2
            yield 3
          }; 

          for (let o of foo()) { 
            console.log(o); 
          }
          // Output:
          // 1
          // 2
          // 3
        }

        Uncaught TypeError: obj is not iterable

        // 普通對象
        const obj = {
          foo'value1',
          bar'value2'
        }
        for(const item of obj){
          console.log(item)
        }
        // Uncaught TypeError: obj is not iterable

        可以看出,for of可以迭代大部分對象甚至字符串,卻不能遍歷普通對象。

        如何用for...of迭代普通對象

        通過前面的基本用法,我們知道,for...of可以迭代數(shù)組、Map等數(shù)據(jù)結構,順著這個思路,我們可以結合對象的Object.values()、Object.keys()、Object.entries()方法以及解構賦值的知識來用for...of遍歷普通對象。

        • Object.values()、Object.keys()、Object.entries()用法及返回值
        const obj = {
          foo'value1',
          bar'value2'
        }
        // 打印由value組成的數(shù)組
        console.log(Object.values(obj)) // ["value1", "value2"]

        // 打印由key組成的數(shù)組
        console.log(Object.keys(obj)) // ["foo", "bar"]

        // 打印由[key, value]組成的二維數(shù)組
        // copy(Object.entries(obj))可以把輸出結果直接拷貝到剪貼板,然后黏貼
        console.log(Object.entries(obj)) // [["foo","value1"],["bar","value2"]]
        • 因為for...of可以迭代數(shù)組和Map,所以我們得到以下遍歷普通對象的方法
        const obj = {
          foo'value1',
          bar'value2'
        }
        // 方法一:使用for of迭代Object.entries(obj)形成的二維數(shù)組,利用解構賦值得到value
        for(const [, value] of Object.entries(obj)){
          console.log(value) // value1, value2
        }

        // 方法二:Map
        // 普通對象轉(zhuǎn)Map
        // Map 可以接受一個數(shù)組作為參數(shù)。該數(shù)組的成員是一個個表示鍵值對的數(shù)組
        console.log(new Map(Object.entries(obj)))

        // 遍歷普通對象生成的Map
        for(const [, value] of new Map(Object.entries(obj))){
          console.log(value) // value1, value2
        }

        // 方法三:繼續(xù)使用for in
        for(const key in obj){
          console.log(obj[key]) // value1, value2
        }

        {
          // 方法四:將【類數(shù)組(array-like)對象】轉(zhuǎn)換為數(shù)組
          // 該對象需具有一個 length 屬性,且其元素必須可以被索引。
          const obj = {
            length3// length是必須的,否則什么也不會打印
            0'foo',
            1'bar',
            2'baz',
            a12  // 非數(shù)字屬性是不會打印的
          };
          const array = Array.from(obj); // ["foo", "bar", "baz"]
          for (const value of array) { 
              console.log(value);
          }
          // Output: foo bar baz
        }
        {
          // 方法五:給【類數(shù)組】部署數(shù)組的[Symbol.iterator]方法【對普通字符串屬性對象無效】
          const iterable = {
            0'a',
            1'b',
            2'c',
            length3,
            [Symbol.iterator]: Array.prototype[Symbol.iterator]
          };
          for (let item of iterable) {
            console.log(item); // 'a', 'b', 'c'
          }
        }

        注意事項

        • 有別于不可終止遍歷的forEach,for...of的循環(huán)可由break, throw, continue 或return終止,在這些情況下,迭代器關閉。
          const obj = {
            foo'value1',
            bar'value2',
            baz'value3'
          }
          for(const [, value] of Object.entries(obj)){
            if (value === 'value2'break // 不會再執(zhí)行下次迭代
            console.log(value) // value1
          };
          [1,2].forEach(item => {
              if(item == 1break // Uncaught SyntaxError: Illegal break statement
              console.log(item)
          });
          [1,2].forEach(item => {
              if(item == 1continue // Uncaught SyntaxError: Illegal continue statement: no surrounding iteration statement
              console.log(item)
          });
          [1,2].forEach(item => {
              if(item == 1return // 仍然會繼續(xù)執(zhí)行下一次循環(huán),打印2
              console.log(item) // 2
          })
        • For…of 與 For…in對比

          • for...in 不僅枚舉數(shù)組聲明,它還從構造函數(shù)的原型中查找繼承的非枚舉屬性;
          • for...of 不考慮構造函數(shù)原型上的不可枚舉屬性(或者說for...of語句遍歷可迭代對象定義要迭代的數(shù)據(jù)。);
          • for...of 更多用于特定的集合(如數(shù)組等對象),但不是所有對象都可被for...of迭代。
            Array.prototype.newArr = () => {};
            Array.prototype.anotherNewArr = () => {};
            const array = ['foo''bar''baz'];
            for (const value in array) { 
              console.log(value); // 0 1 2 newArr anotherNewArr
            }
            for (const value of array) { 
              console.log(value); // 'foo', 'bar', 'baz'
            }

        普通對象為何不能被 for of 迭代

        前面我們有提到一個詞叫“可迭代”數(shù)據(jù)結構,當用for of迭代普通對象時,也會報一個“not iterable”的錯誤。實際上,任何具有 Symbol.iterator 屬性的元素都是可迭代的。我們可以簡單查看幾個可被for of迭代的對象,看看和普通對象有何不同:

        iterator1

        iterator2

        iterator3

        可以看到,這些可被for of迭代的對象,都實現(xiàn)了一個Symbol(Symbol.iterator)方法,而普通對象沒有這個方法。

        簡單來說,for of 語句創(chuàng)建一個循環(huán)來迭代可迭代的對象,可迭代的對象內(nèi)部實現(xiàn)了Symbol.iterator方法,而普通對象沒有實現(xiàn)這一方法,所以普通對象是不可迭代的。

        Iterator(遍歷器)

        關于Iterator(遍歷器)的概念,可以參照阮一峰大大的《ECMAScript 6 入門》——Iterator(遍歷器)的概念:

        iterator

        簡單來說,ES6 為了統(tǒng)一集合類型數(shù)據(jù)結構的處理,增加了 iterator 接口,供 for...of 使用,簡化了不同結構數(shù)據(jù)的處理。而 iterator 的遍歷過程,則是類似 Generator 的方式,迭代時不斷調(diào)用next方法,返回一個包含value(值)和done屬性(標識是否遍歷結束)的對象。

        如何實現(xiàn)Symbol.iterator方法,使普通對象可被 for of 迭代

        依據(jù)上文的指引,我們先看看數(shù)組的Symbol.iterator接口:

        const arr = [1,2,3];
        const iterator = arr[Symbol.iterator]();
        console.log(iterator.next()); // {value: 1, done: false}
        console.log(iterator.next()); // {value: 2, done: false}
        console.log(iterator.next()); // {value: 3, done: false}
        console.log(iterator.next()); // {value: undefined, done: true}

        我們可以嘗試給普通對象實現(xiàn)一個Symbol.iterator接口:

        // 普通對象
        const obj = {
          foo'value1',
          bar'value2',
          [Symbol.iterator]() {
            // 這里Object.keys不會獲取到Symbol.iterator屬性,原因見下文
            const keys = Object.keys(obj); 
            let index = 0;
            return {
              next() => {
                if (index < keys.length) {
                  // 迭代結果 未結束
                  return {
                    valuethis[keys[index++]],
                    donefalse
                  };
                } else {
                  // 迭代結果 結束
                  return { valueundefineddonetrue };
                }
              }
            };
          }
        }
        for (const value of obj) {
          console.log(value); // value1 value2
        };

        上面給obj實現(xiàn)了Symbol.iterator接口后,我們甚至還可以像下面這樣把對象轉(zhuǎn)換成數(shù)組:

        console.log([...obj]); // ["value1", "value2"]
        console.log([...{}]); // console.log is not iterable (cannot read property Symbol(Symbol.iterator))

        我們給obj對象實現(xiàn)了一個Symbol.iterator接口,在此,有一點需要說明的是,不用擔心[Symbol.iterator]屬性會被Object.keys()獲取到導致遍歷結果出錯,因為Symbol.iterator這樣的Symbol屬性,需要通過Object.getOwnPropertySymbols(obj)才能獲取,Object.getOwnPropertySymbols() 方法返回一個給定對象自身的所有 Symbol 屬性的數(shù)組。

        有一些場合會默認調(diào)用 Iterator 接口(即Symbol.iterator方法:

        • 擴展運算符...:這提供了一種簡便機制,可以將任何部署了 Iterator 接口的數(shù)據(jù)結構,轉(zhuǎn)為數(shù)組。也就是說,只要某個數(shù)據(jù)結構部署了 Iterator 接口,就可以對它使用擴展運算符,將其轉(zhuǎn)為數(shù)組(毫不意外的,代碼[...{}]會報錯,而[...'123']會輸出數(shù)組['1','2','3'])。
        • 數(shù)組和可迭代對象的解構賦值(解構是ES6提供的語法糖,其實內(nèi)在是針對可迭代對象Iterator接口,通過遍歷器按順序獲取對應的值進行賦值。而普通對象解構賦值的內(nèi)部機制,是先找到同名屬性,然后再賦給對應的變量。);
        • yield*_yield*后面跟的是一個可遍歷的結構,它會調(diào)用該結構的遍歷器接口;
        • 由于數(shù)組的遍歷會調(diào)用遍歷器接口,所以任何接受數(shù)組作為參數(shù)的場合,其實都調(diào)用;
        • 字符串是一個類似數(shù)組的對象,也原生具有Iterator接口,所以也可被for of迭代。

        迭代器模式

        迭代器模式提供了一種方法順序訪問一個聚合對象中的各個元素,而又無需暴露該對象的內(nèi)部實現(xiàn),這樣既可以做到不暴露集合的內(nèi)部結構,又可讓外部代碼透明地訪問集合內(nèi)部的數(shù)據(jù)。迭代器模式為遍歷不同的集合結構提供了一個統(tǒng)一的接口,從而支持同樣的算法在不同的集合結構上進行操作。

        不難發(fā)現(xiàn),Symbol.iterator實現(xiàn)的就是一種迭代器模式。集合對象內(nèi)部實現(xiàn)了Symbol.iterator接口,供外部調(diào)用,而我們無需過多的關注集合對象內(nèi)部的結構,需要處理集合對象內(nèi)部的數(shù)據(jù)時,我們通過for of調(diào)用Symbol.iterator接口即可。

        比如針對前文普通對象的Symbol.iterator接口實現(xiàn)一節(jié)的代碼,如果我們對obj里面的數(shù)據(jù)結構進行了如下調(diào)整,那么,我們只需對應的修改供外部迭代使用的Symbol.iterator接口,即可不影響外部迭代調(diào)用:

        const obj = {
          // 數(shù)據(jù)結構調(diào)整
          data: ['value1''value2'],
          [Symbol.iterator]() {
            let index = 0;
            return {
              next() => {
                if (index < this.data.length) {
                  // 迭代結果 未結束
                  return {
                    valuethis.data[index++],
                    donefalse
                  };
                } else {
                  // 迭代結果 結束
                  return { valueundefineddonetrue };
                }
              }
            };
          }
        }
        // 外部調(diào)用
        for (const value of obj) {
          console.log(value); // value1 value2
        }

        實際使用時,我們可以把上面的Symbol.iterator提出來進行單獨封裝,這樣就可以對一類數(shù)據(jù)結構進行迭代操作了。當然,下面的代碼只是最簡單的示例,你可以在此基礎上探究更多實用的技巧。

        const obj1 = {
          data: ['value1''value2']
        }
        const obj2 = {
          data: [12]
        }
        // 遍歷方法
        consoleEachData = (obj) => {
          obj[Symbol.iterator] = () => {
            let index = 0;
            return {
              next() => {
                if (index < obj.data.length) {
                  return {
                    value: obj.data[index++],
                    donefalse
                  };
                } else {
                  return { valueundefineddonetrue };
                }
              }
            };
          }
          for (const value of obj) {
            console.log(value);
          }
        }
        consoleEachData(obj1); // value1 value2
        consoleEachData(obj2); // 1  2

        一點補充

        在寫這篇文章時,有個問題給我?guī)砹死_:原生object對象默認沒有部署Iterator接口,即object不是一個可迭代對象。對象的擴展運算符...等同于使用Object.assign()方法,這個比較好理解。那么,原生object對象的解構賦值又是怎樣一種機制呢?

        let aClone = { ...a };
        // 等同于
        let aClone = Object.assign({}, a);

        有一種說法是:ES6提供了Map數(shù)據(jù)結構,實際上原生object對象被解構時,會被當作Map進行解構。關于這點,大家有什么不同的觀點嗎?歡迎評論區(qū)一起探討。

        同時,ECMAScript后面又引入了異步迭代器for await...of 語句,該語句創(chuàng)建一個循環(huán),該循環(huán)遍歷異步可迭代對象以及同步可迭代對象,詳情可查看MDN:for-await...of。

        來源:獨釣寒江雪

        https://segmentfault.com/a/1190000038393650


        瀏覽 16
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            黄色动漫最新动漫免费观看 | nude性旺少妇pics | 妞干网操逼网 | 久久性爱片 | 污动漫在线 | 三级大片视频 | 在线视频欧美日韩 | 极品国产精品 | 巨大胸大乳美女在办公室 | 91人妻无码一区二区三区 |