Iterator

作者: woow_wu7 | 来源:发表于2018-04-21 23:11 被阅读18次

    Iterator遍历器

    Iterator遍历器的作用:

    • (1) 为不同的数据结构,提供统一的访问接口
    • (2) 使得数据接口的成员,能够按某种次序排列
    • (3) 只要部署Iterator接口的数据结构,都能被 for....of 遍历

    Iterator接口的目的

    • iterator接口的目的就是为不同的数据结构,提供统一访问机制,for...of 循环
    • 当使用for...of循环遍历某种数据结构时,该循环会自动的去寻找 iterator 接口
    • 一种数据结构,只要部署了 iterator 接口,我们就认为这种数据结构是可遍历的
    • iterable:是可遍历的意思
    • 有了遍历器接口,数据结构就可以用for...of循环遍历(详见下文),也可以使用while循环遍历。
    iterator: 遍历器
    
    iterable: 可遍历的
    

    遍历器生成函数

    • 遍历器生成函数 的作用时返回一个 遍历器对象
    • 遍历器对象 的本质就是一个指针对象
    • 遍历器对象的特点是 里面有 next的方法,执行next方法,会返回一个对象,该对象包含数据结构的当前成员信息 value 和 done,并且将指针指向数据结构的下一个成员
    • value是当前成员的值
    • done是一个布尔值,表示遍历是否结束
    • 总之,调用指针对象(遍历器对象)的next方法,就可以遍历事先给定的数据结构。

    遍历器对象

    • 遍历器对象的本质就是指针对象
    • 遍历器对象 的本质就是一个指针对象
    • 遍历器对象的特点是 里面有 next的方法,执行next方法,会返回一个对象,该对象包含数据结构的当前成员信息 value 和 done,并且将指针指向数据结构的下一个成员
    • value是当前成员的值
    • done是一个布尔值,表示遍历是否结束
    • 总之,调用指针对象(遍历器对象)的next方法,就可以遍历事先给定的数据结构。

    模拟 - 遍历器生成函数

    
    componentDidMount() {
           function makeIterator(arr) {     // 遍历器生成函数,返回一个遍历器对象 ( 即返回一个指针对象 )
               let indicator = 0;
               return {
                   next: function() {      
                   // 遍历器对象的 next 方法,用来移动指针,返回数据结构相关信息的 对象
                       return indicator < arr.length 
                        ? 
                        { 'value': arr[indicator++], done: false} 
                        : 
                        {'value': undefined, done: true }
                   }
               }
           }
           let it = makeIterator([1,2])   // 执行遍历器生成函数,返回遍历器对象,即指针对象 it 
           console.log( it.next() );      // next方法,用来移动指针,并返回数据结构信息对象
           console.log( it.next() );
           console.log( it.next() );
        }
    
    
    // Object {value: 1, done: false}
    // Object {value: 2, done: false}
    // Object {value: undefined, done: true}
    
    
    ----------------------------------------------------------
    
    
    对于遍历器对象来说, done: false   和   value: undefined   属性都是可以省略的,
    
    因此上面的makeIterator函数可以简写成下面的形式。
    
    function makeIterator(array) {
      var nextIndex = 0;
      return {
        next: function() {
          return nextIndex < array.length ?
            {value: array[nextIndex++]} :
            {done: true};
        }
      };
    }
    
    

    默认 Iterator 接口

    iterator接口的目的,就是为不同的数据结构,提供统一的访问机制,for ... of 循环,当使用 for...of循环 遍历某种数据结构时,该循环会自动的去寻找 iterator 接口,一种数据结构只要部署了 Iterator 接口,我们就称这种数据结构是“可遍历的”(iterable)。

    • ES6规定,默认的 iterator 接口部署在数据结构的 Symbol.iterator属性
    • 一个数据结构只要具有 Symbol.iterator属性 就认为是可遍历的( iterable )
    • Symbol.iterator属性 ------- 本身是一个函数,就是当前数据结构默认的遍历器生成函数
    • Symbol.iterator属性 是 遍历器生成函数
    • 执行遍历器生成函数Symbol.iterator(),返回一个遍历器
    • 注意注意:
      Symbol.iterator本身返回的是Symbol对象的Iterator属性,是一个预设好的Symbol值,作为对象的属性时需要放在 [] 括号内
    
    const obj = {     // obj对象
      [Symbol.iterator] : function () {   // Symbol.iterator是一个Symbol类型的值,作为对象属性要加 []
        return {    // 返回一个遍历器对象
          next: function () { // 遍历器对象的next()方法,返回带有数据机构信息的对象,将指针指向下一个成员
            return {
              value: 1,   // 本例中并没有传入数据结构,而是遍历器对象本身的结构
              done: true
            };
          }
        };
      }
    };
    
    
    
     因为 obj对象 署了 Symbol.iterator 属性,该属性是一个方法,即遍历器生成函数,所以 obj 是可遍历的
    
    
    

    原生具有 Iterator 接口的数据结构

    原生具有iterator接口的数据结构有:数组,字符串,map,set,类数组对象

    原生具有Iterator接口的数据结构,可以直接使用 for...of 循环去遍历

    凡是部署了Symbol.iterator属性的数据结构,就称为部署了遍历器接口。调用这个接口,就会返回一个遍历器对象。

    • array
    • Map
    • Set
    • String
    • TypedArray
    • 函数的 arguments 对象
    • NodeList 对象
    注意!!!!!!!!!!
    
    
    (1) 凡是具有 ( Iterator ) 接口的 数据结构 都可以被  ( for...of ) 遍历
    
    (2) 具有 ( iterator ) 接口的数据结构 可以调用 Symbol.iterator属性 ,是一个方法,返回 遍历器对象
    
    重要!
    
    
    let arr = ['a', 'b', 'c'];
    let iter = arr[Symbol.iterator]();           // 调用数组的 Symbol.iterator 属性方法,返回遍历器对象
    
    iter.next() // { value: 'a', done: false }
    iter.next() // { value: 'b', done: false }
    iter.next() // { value: 'c', done: false }
    iter.next() // { value: undefined, done: true 
    
    
    • 对于原生部署了 iterator 接口的数据结构,不用自己编写遍历器生成函数,for...of循环会自动遍历他们
    • 对于原生没有部署iterator接口的数据结构( 主要是对象 ),都需要自己在Symbol.iterator属性上面部署,这样才会被for...of循环遍历

    对象添加 Iterator 接口

    如果对象想要具有 for...of 循环 调用的 iterator 接口,就必须在 Symbol.iterator 属性上 部署 遍历器生成方法(原型链上具有该方法也可以)

    
    class RangeIterator {
      constructor(start, stop) {    // 构造函数,传入两个参数
        this.value = start;         // this代表实例对象
        this.stop = stop;
      }
    
      [Symbol.iterator]() { return this; }   // 在Symbol.iterator属性上部署了一个方法,返回实例对象
    
      next() {        // 类的next() 方法
        var value = this.value;
        if (value < this.stop) {
          this.value++;
          return {done: false, value: value};
        }
        return {done: true, value: undefined};
      }
    }
    
    function range(start, stop) {   // 定义一个外层函数,返回 构造函数的 实例对象
      return new RangeIterator(start, stop);
    }
    
    for (var value of range(0, 3)) {     // for...of循环,部署了 iteratro接口的 实例对象
      console.log(value); // 0, 1, 2
    }
    
    
    为对象添加 iterator 接口
    
    
    
        componentDidMount() {
            let obj = {
                data: ['hello', 'word'],
                [Symbol.iterator]: function() {   // 在对象的Symbol.iterator属性上部署遍历器生成函数
                    const self = this;    // 绑定里层的this为外层的this,this指向函数运行时所在的对象
                    let index = 0;
                    return {
                        next(){     // 是简写形式,原来是next: function() {......}
                            if(index < self.data.length) {
                                return {
                                    value: self.data[index++],
                                    done: false
                                }
                            } else {
                                return {value: undefined, done: true }
                            }
                            
                        }
                    }
                }
            }
            let xxx = obj[Symbol.iterator]();   // 注意,这里先执行函数,执行时this执行obj,在赋值
            console.log( xxx.next() );   // Object {value: "hello", done: false}
        }
    
    
    

    类似数组对象添加 Iterator

    类似数组的对象,部署 Iterator 接口,简便方法是:Symbol.iterator属性方法 直接引用 数组的 Iterator 接口
    • 什么是类似数组对象?
      对象的键名是数字,并且具有 length 属性
    • 注意,普通对象部署数组的Symbol.iterator方法,并无效果。
    
    
        componentDidMount() {
            let arrObj = {
                '0': 'name',
                '1': 'age',
                'length': 2,
                [Symbol.iterator]: Array.prototype[Symbol.iterator]   
    
                 // 类数组对象部署iterator接口的简便方法: 
                 // Symbol.iterator方法 直接引用数组的 iterator 接口
            }
    
            let xx = arrObj[Symbol.iterator]();    // 调用方法
            console.log( xx.next() , 'xx.next()')  // Object {value: "name", done: false} 
            console.log( xx.next() , 'xx.next()')  // Object {value: "age", done: false} 
            console.log( xx.next() , 'xx.next()')  // Object {value: undefined, done: true} 
    
            for(let i of arrObj) {
                console.log(i,'i')    // name   // age
            }
        }
    
    
    
    • 如果Symbol.iterator方法对应的不是遍历器生成函数(即会返回一个遍历器对象),解释引擎将会报错。
    
    var obj = {};
    
    obj[Symbol.iterator] = () => 1;
    
    [...obj] // TypeError: [] is not a function
    
    
    上面代码中,变量obj的Symbol.iterator方法对应的不是遍历器生成函数,因此报错。
    
    
    ---------------------------------------------------------
    
      componentDidMount() {
            let obj = {
                '0': 'name',
                '1': 'age',
                'length':2
            };
            obj[Symbol.iterator] = Array.prototype[Symbol.iterator];
            for(let i of obj) {
                console.log(i,'i')
            }
           // name i
           // age  i
        }
    
    

    字符串的 iterator 接口

    字符串是类似数组对象,也原生具有 Iterator 接口

    
    var someString = "hi";
    typeof someString[Symbol.iterator]
    // "function"
    
    var iterator = someString[Symbol.iterator]();
    
    iterator.next()  // { value: "h", done: false }
    iterator.next()  // { value: "i", done: false }
    iterator.next()  // { value: undefined, done: true }
    
    

    iterator接口 和 generator函数

    
    let myIterable = {
      [Symbol.iterator]: function* () {       // generator函数,关键词 * 和 yield ,返回的是遍历器对象
        yield 1;
        yield 2;
        yield 3;
      }
    }
    [...myIterable] // [1, 2, 3]
    
    
    --------------------------------------------------------
    
    
    // 或者采用下面的简洁写法
    
    let obj = {
      * [Symbol.iterator]() {
        yield 'hello';
        yield 'world';
      }
    };
    
    for (let x of obj) {
      console.log(x);
    }
    // "hello"
    // "world"
    
    

    遍历器对象的return() 和 throw()

    遍历器对象除了具有 next() 方法,还可以具有 return()throw() 方法
    如果你自己写遍历器对象生成函数,那么next方法是必须部署的,return方法和throw方法是否部署是可选的。

    return() 方法的使用场合
    • 如果 for...of 循环提前退出 ( 通常是因为出错 或者 有break语句,continue语句 ),就会调用 return() 方法
    • 如果一个对象在完成遍历前,需要清理或者释放资源,就可以部署return 方法
    throw() 方法的使用场合
    • throw方法主要是配合 Generator 函数使用,一般的遍历器对象用不到这个方法。
    
    function readLinesSync(file) {
      return {    // 返回一个对象
        [Symbol.iterator]() {       // Symbol.iterator属性:是一个遍历器生成函数
          return {  // 遍历器生成函数返回的是一个遍历器对象,里面有next,return等方法广发
            next() {
              return { done: false };
            },
            return() {
              file.close();
              return { done: true };
            }
          };
        },
      };
    }
    
    
    上面代码中,
    
    函数readLinesSync接受一个文件对象作为参数,返回一个遍历器对象,其中除了next方法,还部署了return方法。
    
    下面的三种情况,都会触发执行return方法。
    
    
    // 情况一
    for (let line of readLinesSync(fileName)) {
      console.log(line);
      break;    // for..of循环中有breack语句,循环提前退出
    }
    
    // 情况二
    for (let line of readLinesSync(fileName)) {
      console.log(line);
      continue;  // for..of循环中有continue语句
    }
    
    // 情况三
    for (let line of readLinesSync(fileName)) {
      console.log(line);
      throw new Error();   // 出错
    }
    
    上面代码中,
    
    情况一输出文件的第一行以后,就会执行return方法,关闭这个文件;
    
    情况二输出所有行以后,执行return方法,关闭该文件;
    
    情况三会在执行return方法关闭文件之后,再抛出错误。
    
    // 注意,return方法必须返回一个对象,这是 Generator 规格决定的。
    // throw方法主要是配合 Generator 函数使用,一般的遍历器对象用不到这个方法。
    
    
    

    for...of循环内部调用的是数据结构的Symbol.iterator方法。

    • for...of循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如arguments对象、DOM NodeList 对象)、 Generator 对象,以及字符串。





    数组

    for...of循环调用遍历器接口,数组的遍历器接口只返回具有数字索引的属性。
    重要!!!
    
    
    let arr = [3, 5, 7];
    
    arr.foo = 'hello';
    
    for (let i in arr) {
      console.log(i); // "0", "1", "2", "foo"
    }
    
    for (let i of arr) {  // for...of循环调用遍历器接口,数组的遍历器接口只返回具有数字索引的属性!!!!
      console.log(i); //  "3", "5", "7"
    }
    
    
    • for...of循环可以代替数组实例的forEach方法。
    
    const arr = ['red', 'green', 'blue'];
    
    for(let v of arr) {  
      console.log(v); // red green blue
    }
    
    const obj = {};
    obj[Symbol.iterator] = arr[Symbol.iterator].bind(arr);  // 对象绑定数组的iterator接口,( 重要!!)
    
    for(let v of obj) {
      console.log(v); // red green blue
    }
    
    
    • for...of 和 for..in 的区别
      在数组中
      for...in循环读取键名。
      for...of循环读取键值。
    
    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
    }
    
    // 在数组中,for...in循环读取键名,for...of循环读取键值
    
    
    • for...of循环调用遍历器接口,数组的遍历器接口只返回具有数字索引的属性。这一点跟for...in循环也不一样。
    
    let arr = [3, 5, 7];
    arr.foo = 'hello';
    
    for (let i in arr) {
      console.log(i); // "0", "1", "2", "foo"
    }
    
    for (let i of arr) {
      console.log(i); //  "3", "5", "7"
    }
    
    





    2018/4/22

    this指向注意点

    // this是函数运行时,所在的对象!!!!!!!!
    
    var A = {
      name: '张三',
      describe: function () {
        return '姓名:'+ this.name;     
      }
    };
    
    var B = {
      name: '李四'
    };
    
    B.describe = A.describe;
    B.describe()     // describe函数运行时,所在的对象是B, this指向B
    // "姓名:李四"
    
    
    ---------------------------------------------------------------------------------
    
    
    var A = {
      name: '张三',
      describe: function () {
        return '姓名:'+ this.name;
      }
    };
    
    var B = {
      name: '李四'
    };
    
    B.describe = A.describe();  // 先运行,在赋值,describe()函数运行时所在对象是A,this指向A
    console.log( B.describe );  // 注意,此时 B.describe是一个对象,而不是函数
    // "姓名:李四"
    
    

    相关文章

      网友评论

        本文标题:Iterator

        本文链接:https://www.haomeiwen.com/subject/nhotlftx.html