JavaScript 进阶营

Iterator

2018-04-21  本文已影响18人  woow_wu7

Iterator遍历器

Iterator遍历器的作用:

Iterator接口的目的

iterator: 遍历器

iterable: 可遍历的

遍历器生成函数

遍历器对象

模拟 - 遍历器生成函数


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)。


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属性的数据结构,就称为部署了遍历器接口。调用这个接口,就会返回一个遍历器对象。

注意!!!!!!!!!!


(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 属性上 部署 遍历器生成方法(原型链上具有该方法也可以)


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 接口


    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
        }
    }



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() 方法的使用场合
throw() 方法的使用场合

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循环调用遍历器接口,数组的遍历器接口只返回具有数字索引的属性。
重要!!!


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"
}


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
}


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循环读取键值


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是一个对象,而不是函数
// "姓名:李四"

上一篇下一篇

猜你喜欢

热点阅读