目录
4、内置迭代器——keys()、values()、entries()
迭代器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
Iterator 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费。
Iterator接口就是对象里面的一个属性,这个属性叫做Symbol.iterator。而原生具备 Iterator 接口的数据结构有Array、Map、Set、String、TypedArray、arguments、NodeList ,它们都可以使用for-of来遍历。
举个🌰:
我们使用for-of来遍历一下数组:
- let arr = [23, 90, 45, 12, 'hello']
- for (let i of arr) {
- console.log(i);
- }
返回结果如下:

数组可以使用for-of实现遍历,是因为数组对象里面有一个Symbol.iterator属性。我们可以打印看一下:

当我们使用for-of来遍历对象时:
- let obj = {
- name: 'zhangsan',
- age: 19
- }
- for (let i of obj) {
- console.log(i);
- }
结果报错了:obj不可迭代

这是因为对象没有Iterator 接口,即对象原型上没有Symbol.iterator属性,所以无法使用for-of来遍历对象。
(1)创建一个指针对象(由Symbol.iterator来创建),指向当前数据结构的起始位置
(2)第一次调用对象的next方法,指针自动指向数据结构的第一个成员
(3)接下来不断调用next方法,指针一直往后移动,直到指向最后一个成员
(4)每次调用next方法都会返回一个包含value和done属性的对象(value 表示当前的值,done 表示遍历是否结束,true表示结束了,false表示还没有结束)
这实际上也是for-of实现遍历的原理。
- let arr = [23, 90, 45, 12, 'hello']
- for (let i of arr) {
- console.log(i);
- }
上面的数组能够实现遍历的原理如下:
- let arr = [23, 90, 45, 12, 'hello']
- // for (let i of arr) {
- // console.log(i);
- // }
-
- // 1.使用Symbol.iterator创建一个迭代器对象
- let iterator = arr[Symbol.iterator]()
- // 2.调用迭代器对象的next方法,每次调用next方法,指针都会往后移动,直到指向最后一个成员
- //每次调用next方法都会返回一个包含value和done属性的对象
- console.log(iterator.next()); //{ value: 23, done: false }
- console.log(iterator.next()); //{ value: 90, done: false }
- console.log(iterator.next()); //{ value: 45, done: false }
- console.log(iterator.next()); //{ value: 12, done: false }
- console.log(iterator.next()); //{ value: "hello", done: false }
- console.log(iterator.next()); //{ value: undefined, done: true }
- console.log(iterator.next()); //{ value: undefined, done: true }
for-of 实现原理就是调用迭代器的next()方法,第一次调用将指针指向数据结构的第一个成员,依次调用依次指向,直到没有成员可以指向,done为true。
要想让对象也能够使用for-of来遍历,那么我们就需要给对象添加一个Iterator 接口,也就是在对象的原型上手动添加一个Symbol.iterator属性。
简单实现思路如下:
1、给对象添加一个iterator接口,即Symbol.iterator属性
2、Symbol.iterator创建一个指针对象,因此我们返回一个对象
3、指针对象里面有一个next方法
4、每次调用next方法返回一个包含value和done属性的对象
- let obj = {
- name: "zhangsan",
- age: 19,
- gender: 'male',
- // 1.给对象添加一个iterator接口,即Symbol.iterator属性
- [Symbol.iterator]() {
- // 2.Symbol.iterator创建一个指针对象,因此我们返回一个对象
- return {
- // 3.指针对象里面有一个next方法
- next: function () {
- // 4.每次调用next方法返回一个包含value和done属性的对象
- return {
- value: '123',
- done: false
- }
- }
- }
- }
- }
-
- for (let i of obj) {
- console.log(i);
- }
5、控制指针往后移动,并添加结束条件
- let obj = {
- name: "zhangsan",
- age: 19,
- gender: 'male',
- // 1.给对象添加一个iterator接口,即Symbol.iterator属性
- [Symbol.iterator]() {
- // 设置索引变量
- let index = 0
- // 2.Symbol.iterator创建一个指针对象,因此我们返回一个对象
- return {
- // 3.指针对象里面有一个next方法
- //这里需要使用箭头函数,否则this指向的就不是obj当前对象了
- next: () => {
- // 4.每次调用next方法返回一个包含value和done属性的对象
- if (index < Object.values(this).length) {
- // Object.values()方法返回对象的属性值构成的数组
- let result = {value: Object.values(this)[index], done: false}
- // 下标自增,让指针往后移动
- index++
- return result
- } else {
- //遍历结束
- return {value: undefined, done: true}
- }
- }
- }
- }
- }
- for (let i of obj) {
- console.log(i);
- }
运行结果如下:
为了更好地访问对象的内容,ES6为数组、Set、Map集合内置了三个API:keys()、values()、和entries() ,它们都返回一个迭代器对象,因为它们都返回一个迭代器对象,因此我们可以使用它们的返回值去调用next()方法,从而实现遍历。其中:
keys() 用来遍历所有的键名;
values() 用来遍历所有的键值;
entries() 用来遍历[键名, 键值]组成的数组。(对于数组,键名就是索引值)
我们以数组为例,使用一下这些方法来实现遍历:
- let arr = ['zhangsan', 'lisi', 'hello']
- let keys = arr.keys();
- let values = arr.values();
- let entries = arr.entries();
-
- console.log(keys.next()); //{ value: 0, done: false }
- console.log(keys.next()); //{ value: 1, done: false }
- console.log(keys.next()); //{ value: 2, done: false }
- console.log(keys.next()); //{ value: undefined, done: true }
-
- console.log(values.next()); //{ value: 'zhangsan', done: false }
- console.log(values.next()); //{ value: 'lisi', done: false }
- console.log(values.next()); //{ value: 'hello', done: false }
- console.log(values.next()); //{ value: undefined, done: true }
-
- console.log(entries.next()); //{ value: [ 0, 'zhangsan' ], done: false }
- console.log(entries.next()); //{ value: [ 1, 'lisi' ], done: false }
- console.log(entries.next()); //{ value: [ 2, 'hello' ], done: false }
- console.log(entries.next()); //{ value: undefined, done: true }
-
- for (let k of keys) {
- console.log(k);
- }
- /*
- 上面的for-of输出结果:
- 0
- 1
- 2
- */
-
- for (let k of values) {
- console.log(k);
- }
- /*
- 上面的for-of输出结果:
- zhangsan
- lisi
- hello
- */
-
- for (let k of entries) {
- console.log(k);
- }
- /*
- 上面的for-of输出结果:
- [ 0, 'zhangsan' ]
- [ 1, 'lisi' ]
- [ 2, 'hello' ]
- */
-
- // 手动调用next 遍历迭代器对象 {value:0 ,done:false}
- let result;
- while (!(result = values.next()).done) {
- console.log(result);
- }
- /*
- 上面的while循环输出结果:
- { value: 'zhangsan', done: false }
- { value: 'lisi', done: false }
- { value: 'hello', done: false }
- */