Loading

Iterator 和 for...of

Iterator 的作用有三个:

  1. 是为各种数据结构,提供一个统一的、简便的访问接
  2. 是使得数据结构的成员能够按某种次序排列
  3. 是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费

部署方式

原生就具有iterator的数据结构

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的 arguments 对象
  • NodeList 对象

ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)。Symbol.iterator属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器。至于属性名Symbol.iterator,它是一个表达式,返回Symbol对象的iterator属性,这是一个预定义好的、类型为 Symbol 的特殊值,所以要放在方括号内

// 为一个对象直接部署
const obj = {
  [Symbol.iterator] : function () {
    return {
      next: function () {
        return {
          value: 1,
          done: true
        };
      }
    };
  }
};

一个数据结构执行的for..of.. 循环,就会执行该数据结构的[Symbol.iterator]()
obj[Symbol.iterator]()执行之后返回一个遍历器对象,其中next()方法用来移动指针,定义每一次遍历的操作
throw()方法
return()方法的使用场合是,如果for...of循环提前退出(通常是因为出错,或者有break语句),就会调用return()方法。如果一个对象在完成遍历前,需要清理或释放资源,就可以部署return()方法。

function readLinesSync(file) {
  return {
    [Symbol.iterator]() {
      return {
        next() {
          return { done: false };
        },
        return() {
          file.close();
          return { done: true };
        }
      };
    },
  };
}

//以下都会调用return方法
// 情况一
for (let line of readLinesSync(fileName)) {
  console.log(line);
  break;
}

// 情况二
for (let line of readLinesSync(fileName)) {
  console.log(line);
  throw new Error();
}

对象部署iterator的方式

使用类

class RangeIterator {
  constructor(start, stop) {
    this.value = start;
    this.stop = stop;
  }

  [Symbol.iterator]() { return this; } // 使用for of 循环是默认调用此方法 返回当前的实例化对象,该对象有一个next方法

  next() { // 返回值定义了该iterator的操作
    var value = this.value;
    if (value < this.stop) {
      this.value++;
      return {done: false, value: value};// done: false 可以省略
    }
    return {done: true, value: undefined };// value: undefined 可以省略
  }
}

function range(start, stop) {
  return new RangeIterator(start, stop);
}

for (var value of range(0, 3)) {
  console.log(value); // 0, 1, 2
}

构造函数的原型链上部署

function Obj(value) {
  this.value = value;
  this.next = null;
}

Obj.prototype[Symbol.iterator] = function() {
  var iterator = { next: next };

  var current = this;

  function next() {
    if (current) {
      var value = current.value;
      current = current.next;
      return { done: false, value: value };
    } else {
      return { done: true };
    }
  }
  return iterator;
}

var one = new Obj(1);
var two = new Obj(2);
var three = new Obj(3);

one.next = two; // 修改指针指向 保证Iterator的功能
two.next = three;

for (var i of one){
  console.log(i); // 1, 2, 3
}

类数组部署

类似数组的对象(存在数值键名和length属性),部署 Iterator 接口,有一个简便方法,就是Symbol.iterator方法直接引用数组的 Iterator 接口。
或者直接转换成为类数组

  • 普通对象部署数组的Symbol.iterator方法,并无效果
NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
// 或者
NodeList.prototype[Symbol.iterator] = [][Symbol.iterator];

[...document.querySelectorAll('div')] // 可以执行了
posted @ 2021-01-20 10:37  不吃苦瓜^  阅读(59)  评论(0编辑  收藏  举报