14、ES6 for...of循環

 for...of循環

          ES6借鑑C++、Java、C#和Python語言,引入了ˆ‘for...of 循環,作爲遍歷所有數據結構的統一的方法。一個數據結構只要部署了›„‘ŽSymbol.iterator 屬性,就被視爲具有iterator接口,就可以用ˆ‘ˆ‘for...of循環遍歷它的成員。也就是說,ˆ‘for...of 循環內部調用的是數據結構的›„‘Ž›„‘ŽSymbol.iterator 方法。

for...of循環可以使用的範圍包括數組、Set和Map結構、某些類似數組的對象(比如arguments對象、DOMNodeList對象)、後文的Generator對象,以及字符串。


1、數組

       數組原生具備iterator接口,ˆ‘for...of循環本質上就是調用這個接口產生的遍歷器,可以用下面的代碼證明。   

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>demo</title>
    <script src="js/traceur.js"></script>
    <script src="js/bootstrap.js"></script>
    <script type="text/traceur">
       const arr=['red','green','blue'];
       let iterator=arr[Symbol.iterator]();

       for (let v of arr){
           console.log(v);
           //red
           //green
           //blue
       }

       for (let vv of iterator){
            console.log(vv);
             //red
             //green
             //blue
       }
       
    </script>
</head>
<body>
     
</body>
</html>

上面代碼的ˆ‘ˆ‘for...of 循環的兩種寫法是等價的。


for...of循環可以代替數組實例的 forEach 方法。…

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>demo</title>
    <script src="js/traceur.js"></script>
    <script src="js/bootstrap.js"></script>
    <script type="text/traceur">
      const arr=['red','green','blue'];
      arr.forEach(function (element,index){
          console.log(element);
          console.log(index);
          //red
          //0
          //green
          //1
          //blue
          //2
      });

    </script>
</head>
<body>
     
</body>
</html>

JavaScript原有的for...in循環,只能獲得對象的鍵名,不能直接獲取鍵值。ES6提供ˆ‘for...of‘ˆ 循環,允許遍歷獲得鍵值。ƒ

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>demo</title>
    <script src="js/traceur.js"></script>
    <script src="js/bootstrap.js"></script>
    <script type="text/traceur">
      var arr=['a','b','c','d'];

      for (let a in arr){
          console.log(a); //0 1 2 3
      }

      for (let a of arr){
          console.log(a); //a b c d
      }

    </script>
</head>
<body>
     
</body>
</html>

上面代碼表明,for...in循環讀取鍵名,for...of‘ˆ 循環讀取鍵值。如果要通過ˆ‘for...of 循環,獲取數組的索引,可以藉助數組實例的 entries‡‹‡• 方法和‡›• keys 方法。

ˆ‘

for...of循環調用遍歷器接口,數組的遍歷器接口只返回具有數字索引的屬性。這一點跟for...in循環也不一樣。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>demo</title>
    <script src="js/traceur.js"></script>
    <script src="js/bootstrap.js"></script>
    <script type="text/traceur">
     let arr=[3,5,7];
     arr.foo='hello';

     for (let i in arr){
         console.log(i); //0 1 2 3 foo
     }
      
      for (let i of arr){
          console.log(i);  //3 5 7
      }
    </script>
</head>
<body>
     
</body>
</html>

上面代碼中,ˆ‘for...of循環不會返回數組ƒ  arr 的ˆ‘‘ foo 屬性。


2、Set和Map結構

      Set和Map結構也原生具有Iterator接口,可以直接使用ˆ‘for...of 循環。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>demo</title>
    <script src="js/traceur.js"></script>
    <script src="js/bootstrap.js"></script>
    <script type="text/traceur">
     var engines=new Set(["Gecko","Trident","Webkit","Webkit"]);
     for (var e of engines){
         console.log(e); //Gecko Trident Webkit
     }

     var es6=new Map();
     es6.set("edition",6);
     es6.set("committee","TC39");
     es6.set("standard","ECMA-262");
     for (var [name,value] of es6){
         console.log(name + ":" +value);
         //edition:6
         //committee:TC39
         //standard:ECMA-262
     }
    </script>
</head>
<body>
     
</body>
</html>

上面代碼演示瞭如何遍歷Set結構和Map結構。值得注意的地方有兩個,首先,遍歷的順序是按照各個成員被添加進數據結構的順序。其次,Set結構遍歷時,返回的是一個唯一的值,而Map結構遍歷時,返回的是一個數組,該數組的兩個成員分別爲當前Map成員的鍵名和鍵值。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>demo</title>
    <script src="js/traceur.js"></script>
    <script src="js/bootstrap.js"></script>
    <script type="text/traceur">
     let map=new Map().set('a',1).set('b',2);
     for (let pair of map){
         console.log(pair);
         //["a", 1]
         //["b", 2]
     }

     for (let [key,value] of map){
         console.log(key +':'+value);
         //a:1
         //b:2
     }
    </script>
</head>
<body>
     
</body>
</html>

-------------------------------------------------------------

計算生成的數據結構

       有些數據結構是在現有數據結構的基礎上,計算生成的。比如,ES6的數組、Set、Map都部署了以下三個方法,調用後都返回遍歷器對象。

      (1)、entries() 返回一個遍歷器對象,用來遍歷[鍵名, 鍵值] 組成的數組。對於數組,鍵名就是索引值;對於Set,鍵名與鍵值相同。Map結構的iterator接口,默認就是調用entries方法。

      (2)、 ‡›•keys()返回一個遍歷器對象,用來遍歷所有的鍵名。

      (3)、values()返回一個遍歷器對象,用來遍歷所有的鍵值。

這三個方法調用後生成的遍歷器對象,所遍歷的都是計算生成的數據結構。­

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>demo</title>
    <script src="js/traceur.js"></script>
    <script src="js/bootstrap.js"></script>
    <script type="text/traceur">
      let arr=['a','b','c'];
      for (let pair of arr.entries()){
          console.log(pair);
          //[0, "a"]
          //[1, "b"]
          //[2, "c"]
      }

      for (let pair of arr.keys()){
          console.log(pair); //0 1 2
      }

      for (let pair of arr.values()){
          console.log(pair); //a b c
      }
    </script>
</head>
<body>
     
</body>
</html>


--------------------------------------------------------------------

類似數組的對象

       類似數組的對象包括好幾類。下面是for…of循環用於字符串、DOM NodeList對象、arguments對象的例子。

//字符串

<!DOCTYPE html>

<html lang="en">

<head>

   <meta charset="UTF-8">

   <title>demo</title>

   <script src="js/traceur.js"></script>

   <script src="js/bootstrap.js"></script>

   <script type="text/traceur">

      let str="hello";

      for(let s of str){

        console.log(s);

       //h

       //e

       //l

       //l

       //o

      }

   </script>

</head>

<body>

    

</body>

</html>


// ‘†‡‹•對象DOM NodeList對象

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>demo</title>
    <script src="js/traceur.js"></script>
    <script src="js/bootstrap.js"></script>
    <script type="text/traceur">
      let paras=document.querySelectorAll("p");
      for (let p of paras){
          p.classList.add("test");
      }
    </script>
</head>
<body>
     
</body>
</html>

//argument對象

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>demo</title>
    <script src="js/traceur.js"></script>
    <script src="js/bootstrap.js"></script>
    <script type="text/traceur">
      function printArgs(){
          for (let x of arguments){
              console.log(x);
          }
      }
      printArgs('a','b');
      //a
      //b
    </script>
</head>
<body>
     
</body>
</html>

對於字符串來說,for...of循環還有一個特點,就是會正確識別32位UTF­16字符。


並不是所有類似數組的對象都具有iterator接口,一個簡便的解決方法,就是使用Array.from方法將其轉爲數組。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>demo</title>
    <script src="js/traceur.js"></script>
    <script src="js/bootstrap.js"></script>
    <script type="text/traceur">
      let arrayLike={length:2,0:'a',1:'b'};
      /*for (let x of arrayLike){
          console.log(x);//報錯arrayLike[Symbol.iterator] is not a function
      } */

      for (let x of Array.from(arrayLike)){
          console.log(x);  
          //a
          //b
          //因for...of循環輸出的是值,而且是索引爲數字的值,故鍵名爲length的值2不輸出
      }
    </script>
</head>
<body>
     
</body>
</html>

------------------------------------------------------------------------------

對象

    對於普通的對象,for...of 結構不能直接使用,會報錯,必須部署了iterator接口後才能使用。但是,這樣情況下,for...in 循環依然可以用來遍歷鍵名。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>demo</title>
    <script src="js/traceur.js"></script>
    <script src="js/bootstrap.js"></script>
    <script type="text/traceur">
      var es6={
          edition:6,
          committee:"TC39",
          standard:"ECMA-262"
      };

      for (e in es6){
          console.log(e);
          //edition
          //committee
          //standard
      }

      for (e of es6){
          console.log(e); //報錯TypeError: es6[Symbol.iterator] is not a function
      }
    </script>
</head>
<body>
     
</body>
</html>

上面代碼表示,對於普通的對象,ˆ‘for...in循環可以遍歷鍵名,ˆ‘for...of循環會報錯。一種解決方法是,使用„Œ‡…Object.keys 方法將對象的鍵名生成一個數組,然後遍歷這個數組。ˆ

for (var key of Object.keys(someObject)){
         console.log(key +":"+someObject[key]);
     }

在對象上部署iterator接口的代碼,一個方便的方法是將數組的›„‘ŽSymbol.iterator屬性,直接賦值給其他對象的›„‘Ž›„‘ŽSymbol.iterator 屬性。比如,想要讓ˆ‘for...of 環遍歷jQuery對象,只要加上下面這一行就可以了。Œ‡›

jQuery.prototype[Symbol.iterator]=Array.prototype[Symbol.iterator];

另一個方法是使用Generator函數將對象重新包裝一下。

function* entries(obj){
         for (let key of Object.keys[obj]){
             yield [key,obj[key]];
         }
     }
     for (let [key,value] of entries(obj)){
         console.log(key,"->",value);
     }

--------------------------------------------------------------

與其他遍歷語法的比較

      以數組爲例,JavaScript提供多種遍歷語法。最原始的寫法就是for循環。

for (var index=0;index < myArray.length;index++){
         console.log(myArray[index]);
     }

這種寫法比較麻煩,因此數組提供內置的forEach方法。

 myArray.forEach(function (value){
          console.log(value);
     });

這種寫法的問題在於,無法中途跳出ˆ‘ƒ…Š forEach 循環,break命令或return命令都不能奏效。ˆ‘


for...in 循環可以遍歷數組的鍵名。ˆ

for (var index in myArray){
        console.log(myArray[index]);
    }

for...in循環有幾個缺點。

      (1)、數組的鍵名是數字,但是for...in循環是以字符串作爲鍵名“0”、“1”、“2”等等。

      (2)、for...in循環不僅遍歷數字鍵名,還會遍歷手動添加的其他鍵,甚至包括原型鏈上的鍵。

      (3)、某些情況下,for...in循環會以任意順序遍歷鍵名。

總之,for...in循環主要是爲遍歷對象而設計的,不適用於遍歷數組。ˆ‘


for...of循環相比上面幾種做法,有一些顯著的優點。ˆ

for (let value of myArray){
        console.log(value);
    }

(1)、有着同for...in一樣的簡潔語法,但是沒有for...in那些缺點。

(2)、不同用於forEach方法,它可以與break、continue和return配合使用。

(3)、提供了遍歷所有數據結構的統一操作接口。


下面是一個使用break語句,跳出for...of循環的例子。

for (var n of fibonacci){
        if (n >1000){
            break;
        }
        console.log(n);
    }

上面的例子,會輸出斐波納契數列小於等於1000的項。如果當前項大於1000,就會使用break語句跳出for...of循環。








發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章