14、ES6 for...of循环

来源:互联网 发布:中石油国际事业部 知乎 编辑:程序博客网 时间:2024/06/16 04:23

 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循环。








原创粉丝点击