Skip to content

迭代器

遍历器

现在很多语言中都存在 Iterator 遍历器(迭代器),目的是为了是用统一接口处理不同数据类型。在 JS 中包括 Array/Object/Set/Map 等多种集合数据结构,迭代器就是为了采用统一的方式遍历数据。

常用的解构与扩展运算符内部也在使用遍历器实现。

解决的问题

以往遍历数据时比如使用 for 需要变量来记录每次迭代数据的位置,如果遍历多层需要设置多个变量用于记录并不方便。

原始实现

下面使用以往掌握的知识实现生成器,有助于更好的理解生成器。

javascript
function generator(arr) {
  let i = 0;
  return {
    next() {
      let done = arr.length <= i;
      return {
        value: done ? undefined : arr[i++],
        done: done
      };
    }
  };
}
let gen = new generator([1, 2, 3]);
console.log(gen.next()); //{value: 1, done: false}
console.log(gen.next()); //{value: 2, done: false}
console.log(gen.next()); //{value: 3, done: false}
console.log(gen.next()); //{value: undefined, done: false}

原理分析

默认情况下对象是不可以迭代的,当添加[Symbol.iterator]生成器后,就会变为可迭代对象。

javascript
let object = {
  data: [],
  *[Symbol.iterator]() {
    for (const iterator of this.data) {
      yield iterator;
    }
  },
  push(item) {
    this.data.push(item);
  }
};
object.push("2");
object.push("1");
for (const iterator of object) {
  console.log(iterator);
}

数据结构本身或在原型链上包含 Symbol.iterator 遍历器生成方法时即为可迭代对象。Symbol.iterator 返回next 方法。

javascript
let obj = {
  data: [1, 2, 3, 4],
  pos: 0,
  [Symbol.iterator]() {
    self = this;
    return {
      next() {
        let value = self.data[self.pos];
        let done = ++self.pos > self.data.length;
        return { value, done };
      }
    };
  }
};
// let iterator = obj[Symbol.iterator]();
// console.log(iterator.next());
for (const iterator of obj) {
  console.log(iterator);
}

如果对象是数字键并包含length可以直接引用数组迭代器

javascript
let object = {
  0: "1",
  1: "2",
  2: "3",
  length: 3,
  [Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (const iterator of object) {
  console.log(iterator);
}

带终止的迭代器

js
// 案例: 创建一个教室类, 创建出来的对象都是可迭代对象
class Classroom {
  constructor( students) {
    this.students = students
  }
  entry(newStudent) {
    this.students.push(newStudent)
  }
  // 实现迭代器
  [Symbol.iterator]() {
    let index = 0
    return {
      next: () => {
        if (index < this.students.length) {
          return { done: false, value: this.students[index++] }
        } else {
          return { done: true, value: undefined }
        }
      },
      return: () => {    //监听迭代器停止
        console.log("迭代器提前终止了~")
        return { done: true, value: undefined }
      }
    }
  }
}
 
const classroom = new Classroom( ["学生1", "学生2", "学生3", "学生4", "学生5"])
classroom.entry("lilei")
 
for (const stu of classroom) {
  console.log(stu)
  if (stu === "学生4") break
}
 
// 打印
  // 学生1
  // 学生2
  // 学生3
  // 学生4
  // 迭代器提前终止了~

系统内置的 Array/Set/String/Map/NodeList 对象都已经内置了迭代器,下面是体验数组迭代器的使用。

javascript
let arr = [1, 2, "4", "6"];
const iterator = arr[Symbol.iterator]();
console.log(iterator.next());

字符串同样实现了遍历器

javascript
let hd = "love";
let iterator = hd[Symbol.iterator]();
console.log(iterator.next());
console.log(iterator.next());

再来看看 set 类型同样实现了遍历器

javascript
let hd = new Set().add("2").add("3");
let f = hd[Symbol.iterator]();
console.log(f.next());

解构与扩展

常用的解构与扩展运算符内部也在使用遍历器实现。

数组扩展调用遍历器

javascript
let arr = ["2", "3"];
console.log([1, 2, 3, ...arr]);

解构内部也是使用遍历器

javascript
let hd = new Set().add("3").add("4");
[a, b] = hd;
console.log(a, b); //3 4

for/of

拥有迭代特性即包含 Symbol.iterator 方法的数据结构,就可以使用for/of 遍历操作。