ES6 之迭代器


作者:Seiya

时间:2019年08月26日


迭代器


JavaScript 原有的表示“集合”的数据结构,主要是数组(Array)和对象(Object),ES6 又添加了Map和Set。这样就需要一种统一的接口机制,来处理所有不同的数据结构。遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。

任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。


tips

所谓迭代器,其实就是一个具有 next() 方法的对象,每次调用 next() 都会返回一个结果对象,该结果对象有两个属性,value 表示当前的值,done 表示遍历是否结束。




迭代器的作用

  • 为各种数据结构,提供一个统一的、简便的访问接口;

  • 使得数据结构的成员能够按某种次序排列;

  • ES6 创造了一种新的遍历命令 for...of 循环,Iterator 接口主要供 for...of 消费;




可迭代类型

ES6 引入了一个新的 Symbol 对象,symbol 值是唯一的。定义了一个 Symbol.iterator 属性,只要对象中含有这个属性,就是可迭代的,可用于for...of。




原生具备iterator接口的数据(可用for of遍历)

  • Array

  • set容器

  • map容器

  • String

  • 函数的 arguments 对象

  • NodeList 对象


如下所示:

let str = 'abcd';
let arr = [1, 2, 'kobe', true];

for(let item of str){
  console.log(item);  // a b c d
}

for(let i of arr){
  console.log(i);     // 1 2 kobe true
}

function fun() {
  for (let i of arguments) {
    console.log(i)    // 1 4 5
  }
}
fun(1, 4, 5)

tips

在ES6中,所有的集合对象,包括数组,Map和Set,还有字符串都是可迭代的,因为他们都有默认的迭代器。




迭代器的工作原理

  • 创建一个指针对象,指向数据结构的起始位置;

  • 第一次调用next方法,指针自动指向数据结构的第一个成员;

  • 接下来不断调用next方法,指针会一直往后移动,直到指向最后一个成员;

  • 每调用 next 方法返回的是一个包含 value 和 done 的对象,{ value: 当前成员的值, done: 布尔值 }

    • value 表示当前成员的值,done 对应的布尔值表示当前的数据的结构是否遍历结束;

    • 当遍历结束的时候返回的 value 值是 undefined,done 值为 true;




内建迭代器

为了更好的访问对象中的内容,比如有的时候我们仅需要数组中的值,但有的时候不仅需要使用值还需要使用索引,ES6 为数组、Map、Set 集合内建了以下三种迭代器:

  1. entries() 返回一个遍历器对象,用来遍历[键名, 键值]组成的数组。对于数组,键名就是索引值。

  2. keys() 返回一个遍历器对象,用来遍历所有的键名。

  3. values() 返回一个遍历器对象,用来遍历所有的键值。


举例如下:

var colors = ["red", "green", "blue"];

for (let index of colors.keys()) {
  console.log(index);
}

// 0
// 1
// 2

for (let color of colors.values()) {
  console.log(color);
}

// red
// green
// blue

for (let item of colors.entries()) {
  console.log(item);
}

// [ 0, "red" ]
// [ 1, "green" ]
// [ 2, "blue" ]

注意:

Set 类型的 keys() 和 values() 返回的是相同的迭代器,这也意味着在 Set 这种数据结构中键名与键值相同。


tips

在 for-of 循环中,如果没有显式指定则使用默认的迭代器。数组和 Set 集合的默认迭代器是 values() 方法,Map 集合的默认迭代器是 entries() 方法。




手写迭代器

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

function createIterator(items) {
  var i = 0;
  return {
    next: function() {
      var done = i >= items.length;
      var value = !done ? items[i++] : undefined;

      return {
        done: done,
        value: value
      };
    },
    return: function() {
      console.log("执行了 return 方法");
      return {
        value: 404,
        done: true
      };
    }
  };
}

tips

return 方法的使用场合是,如果 for...of 循环提前退出(通常是因为出错,或者有 break 语句或 continue 语句),就会调用 return 方法。如果一个对象在完成遍历前,需要清理或释放资源,就可以部署 return 方法。




注意

  • for of循环不支持遍历普通对象

    对象的Symbol.iterator属性,指向该对象的默认遍历器方法。当使用for of去遍历某一个数据结构的时候,首先去找Symbol.iterator,找到了就去遍历,没有找到的话不能遍历,提示Uncaught TypeError: XXX is not iterable


  • 当使用扩展运算符(...)或者对数组和 Set 结构进行解构赋值时,会默认调用 Symbol.iterator 方法
最后更新时间: 2019-8-26 15:38:24