zoukankan      html  css  js  c++  java
  • 理解ES6中的Iterator

    一、为什么使用Iterator

           我们知道,在ES6中新增了很多的特性,包括Map,Set等新的数据结构,算上数组和对象已经有四种数据集合了,就像数组可以使用forEach,对象可以使用for...in.. 进行遍历一样,是不是随着Map和Set的出现也应该给这两种集合一个遍历方法呢?如果这样的话js的方法对象就太多了,既然这四种集合都是需要遍历的,那么完全可以用一个统一的访问机制。于是乎Iterator应运而生。

     

    二、Iterator是什么

       Iterator是一个访问机制,同时是一个接口,可以实现在不同的数据集合中,完成它的功能。

      Iterator本质是一个指针对象,其中包含一个next方法,这个方法可以改变指针的指向,并且返回一个包含value和done的对象,value为当前所指向的成员的值,done表示是否遍历完成。通过这些描述,我们大致可以手写一个Iterator,用于遍历数组:

    // 定义遍历器对象
    let Iterator = function (arr) {
      let index = 0;  // 当前指针
      return {
        next() {
          return index < arr.length ? {value: arr[index++],done: false} : {value: undefined, done: true};
        }
      }
    };
    let arr = [1, 2, 3];
    let it = Iterator(arr);
    console.log(it.next()); // { value: 1, done: false }
    console.log(it.next()); // { value: 2, done: false }
    console.log(it.next()); // { value: 3, done: false }
    console.log(it.next()); // { value: undefined, done: true }

     

    三、ES6中的Iterator接口的实现

      我们知道Iterator的接口就是提供了一个统一的访问机制,如果我们像上面那样,不断的调用next()才能遍历完成,如果Iterator像java那样提供一个hasNext()方法的话,那么我们可以通过while进行遍历,事实上js中是没有的。之所以没有是因为ES6使用for...of...实现了对具有Symbol.iterator(可遍历)的数据结构的遍历,也就是说只要是包含Symbol.iterator属性的结构都可以使用for...of...进行遍历。接下来我们看看我们直接输出上面四种结构的Symbol.iterator属性是什么样子。

    let a = [1,2,3];
    let it_arr = a[Symbol.iterator]();
    it_arr.next();  // { value: 1, done: false }
    it_arr.next();  // { value: 2, done: false }
    it_arr.next();  // { value: 3, done: false }
    it_arr.next();  // { value: undefined, done: true }
    
    let b = new Set(["a","b","c"]);
    let it_set = b[Symbol.iterator]();
    it_set.next(); // { value: 1, done: false }
    it_set.next(); // { value: 2, done: false }
    it_set.next(); // { value: 3, done: false }
    it_set.next(); // { value: undefined, done: true }
    
    let c = new Map([["a","1"]]);
    let it_map =c[Symbol.iterator]();
    it_map.next();  // { value: [ 'a', '1' ], done: false }
    it_map.next();  // { value: undefined, done: true }
    
    let d = new Object();
    d.name = "Jyy";
    console.log(d[Symbol.iterator]());  // TypeError: d[Symbol.iterator] is not a function

      上面是ES6中在四种数据集合中的Iterator的实现。可以看到ES6并没有在对象中原生部署Symbol.iterator属性,因此我们需要手动在对象中设置遍历器属性。下面就是一个简单的在一个对象中设置Symbol.iterator属性的例子。

    function Demo(list){
      this.list = list;
      this[Symbol.iterator] = function(){
        let index = 0;
        return {
          next: function(){
            return index < list.length ? {value: list[index++], done: false} : {value: undefined, done: true};
          }
        }
      }
    }
    let demo = new Demo([1,2,3,4]);
    for(let i of demo){
      console.log(i); // 1 2 3 4
    }

      上面的代码就是手动在Demo对象中添加了一个遍历器对象,在next中实现遍历的逻辑,然后就可以使用for...of...进行遍历。当然我们也可以将遍历器对象部署在Demo的原型对象,for...of...依然会自动调用。

      另外,对于类数组的对象(key值为数字索引,包含lenth属性),我们可以直接将数组的遍历器对象赋值给对象,代码如下:

    let obj = {
      0 : "a",
      1 : "b",
      2 : "c",
      length :3,
    }
    obj[Symbol.iterator] = Array.prototype[Symbol.iterator];
    for(let i of obj){
      console.log(i); // a b c
    }

      除此之外,String,函数的arguments对象都原生部署了遍历器接口

    // 字符串可用for...of...进行遍历
    let str = "123";
    for(let s of str){
      console.log(s); // 1 2 3
    }
    
    // 函数中的arguments对象可以使用for...of...进行遍历
    function demo(){
      for(let i of arguments){
        console.log(i); // 1,2,3
      }
    }
    demo(1,2,3)

    四、使用Iterator接口的场合

      除了for...of...,es6中还有哪些语法用到了iterator接口呢?

      1. 解构赋值

    function Demo(list){
      this.list = list;
      this[Symbol.iterator] = function(){
        let index = 0;
        return {
          next: function(){
            index++;
            return index < list.length ? {value: list[index++], done: false} : {value: undefined, done: true};
          }
        }
      }
    }
    [a,...b] = new Demo([1,2,3,4]);
    console.log(b);  // [2,3,4]

        如上代码中每次返回的value都是1,我们在中括号(注意不是花括号)中使用结构赋值,发现b的值为[2,3,4]

      2.扩展运算符

        与解构赋值相反的扩展运算符,也使用了遍历器。   

    function Demo(list){
      this.list = list;
      this[Symbol.iterator] = function(){
        let index = 0;
        return {
          next: function(){
            return index < list.length ? {value: list[index++], done: false} : {value: undefined, done: true};
          }
        }
      }
    }
    let demo = new Demo([1,2,3,4]);
    console.log([0, ...demo, 5]); //[ 0, 1, 2, 3, 4, 5 ]

    五、与其他遍历语法的比较

      在js中的遍历语法有:for循环,for...in..., forEach

      1. for循环的缺点我们都知道,就是书写起来太麻烦,如果有快捷键的方式的话还好,如果没有,就要写很多的代码,而且看起来也不爽。

      2. forEach书写起来比较简单,但是它的问题是无法中途跳出循环,break、return、continue等都不能起作用

      3. for...in...是专门为遍历对象而设计的,如果使用这种方法遍历数组,key则会变为字符串,如下:

    let arr = [1,2,3];
    for(let a in arr){
      console.log(typeof a);  // string
    }

      另外我们可能在开发中遇到的问题就是,使用for...in...的话,那么会遍历原型链上的键

    let person = {
      age :25
    }
    person.prototype={
      name : "jyy"
    }
    for(let i in Person){
      console.log(i); // age prototype
    }
  • 相关阅读:
    前端笔记-jquery
    git的使用
    前端笔记-bom
    微信小程序没找到构建npm或者没找到node_modules目录
    微信小程序判断 wx:if wx:else
    微信小程序提示云函数部署不成功
    cmd如何进入文件夹
    微信小程序view居中
    vue页面跳转兄弟组件传值
    vue全局变量apiurl
  • 原文地址:https://www.cnblogs.com/jyybeam/p/12019788.html
Copyright © 2011-2022 走看看