ES6将迭代器和生成器内置到语言中,不仅简化了数据处理和集合操作,还弥补了for、while等普通循环的不足,例如难以遍历无穷集合或自定义的树结构等。

  迭代器(Iterator)是一种用于迭代的对象,可有序的依次访问集合中的数据项。ES6制订了一套标准化的迭代器接口(包含3个方法,如表11所列),只要实现了这套接口都能成为迭代器。

表11 迭代器接口

方法 返回值 描述
next() IteratorResult 必选,获取下一个迭代器结果
return() IteratorResult 可选,停止迭代并返回一个迭代器结果
throw() IteratorResult 可选,抛出错误并返回一个迭代器结果

  上表中的IteratorResult也叫迭代器结果,是一个特定形式的对象,它必须包含两个属性:value和done。value属性就是集合成员的值,done属性是一个布尔值,用于标记当前迭代是否结束。下面是一个简单的迭代器示例。

function createIterator(items) {
  var index = 0;
  return {
    next() {
      var done = index >= items.length;
      return { value: items[index++], done: done };
    }
  };
}
var iterator = createIterator(["a", "b"]);
iterator.next();        //{value: "a", done: false}
iterator.next();        //{value: "b", done: false}
iterator.next();        //{value: undefined, done: true}

  当迭代结束时,done属性的值将会是true,而value属性在未提供返回值的时候就会设为undefined。

一、可迭代对象

  包含Symbol.iterator属性的对象被称为可迭代对象(Iterable),Symbol.iterator是一个特殊的内置符号(详见第6篇),它的值是一个返回迭代器的方法。ES6内置了多种可迭代对象,例如集合(Array、TypedArray、Set和Map)、类数组对象(Arguments和NodeList)、字符串等,它们都含有各自默认的迭代器。下面的示例使用了数组的默认迭代器,通过next()方法依次遍历了它所包含的元素,返回结果与上一个示例类似。

var arr = ["a", "b"],
  iterator = arr[Symbol.iterator]();
iterator.next();        //{value: "a", done: false}
iterator.next();        //{value: "b", done: false}
iterator.next();        //{value: undefined, done: true}

  可迭代对象的应用场景在前面的章节中已陆续介绍过,例如第2篇的扩展运算符、第3篇的解构赋值和第12篇的Array.from()方法等。在接下来的章节中,还会将可迭代对象应用于for-of循环和yield*。

二、for-of

  这是ES6新增的一种循环语句,当要遍历一个可迭代对象时,会先通过它的Symbol.iterator属性得到默认迭代器,再调用迭代器的next()方法,读取IteratorResult的value属性的值并赋给for-of语句中声明的变量,如此反复,直到done属性为ture时才终止遍历。而和其它循环语句一样,for-of循环也能通过跳转语句return、break和continue提前终止。下面分别对Set和Map两种数据结构进行for-of循环。

var set = new Set(["strick", 28]),
  map = new Map([["name", "strick"], ["age", 28]]);
/********************
  "strick"
  28
********************/
for (var value of set) {
  console.log(value);
}
/********************
  "name" "strick"
  "age" 28
********************/
for (var [key, value] of map) {
  console.log(key, value);
}

  在前文中曾多次提到过3个迭代器方法,根据上面代码的输出结果可知,Set和Map的默认迭代器分别通过values()和entries()方法获得。

三、字符串

  字符串虽然是基础类型,但它能被隐式的封装成String对象,而String含有默认迭代器,因此可以进行for-of循环。在第9篇字符串中曾提到过,JavaScript采用了UTF-16编码的Unicode字符集,在BMP中的字符可用一个编码单元表示,而在辅助平面中的字符则需要两个编码单元。以“向”和“𠮳”为例,前者存在于BMP中,后者存在于辅助平面中,当用for循环遍历下面的这段字符串(str)时,无法得到正确的字符。

var str = "向𠮳";        //str.length的值为3
for (var i = 0; i < str.length; i++) {
  console.log(str[i]);
}

  而在改用for-of循环后(如下代码所示),就能得到预期的结果。由此可见,迭代器在处理字符串时更加安全可靠。

for (var value of str) {
  console.log(value);
}

四、创建可迭代对象

  普通的对象只要包含Symbol.iterator属性,并返回一个迭代器,就能摇身一变成为可迭代对象。在下面的代码中,iterable对象本身就是迭代器,因此Symbol.iterator属性的返回值可以是当前对象。注意,必须返回迭代器,否则会出现不可预料的错误。

var iterable = {
  items: ["a", "b"],
  index: 0,
  [Symbol.iterator]() {
    return this;
  },
  next() {
    var done = this.index >= this.items.length;
    return { value: this.items[this.index++], done: done };
  },
  return() {
    return { value: undefined, done: true };
  }
};

  在iterable对象中,包含return()方法,只有当迭代器终止遍历时,才会触发此方法。例如在for-of循环体中执行break、continue或return语句,甚至还能通过调用throw语句抛出自定义异常,提前结束代码的运行,以此实现触发条件,如下所示。

for (var value of iterable) {
  throw new Error();
}

 

 posted on 2019-04-01 09:20  咖啡机(K.F.J)  阅读(394)  评论(0编辑  收藏  举报