Iterator

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)