面试题,给出一个对象,如何封装一个类使其可遍历,就是算是手写一个Generator函数吧
1 | var test = { |
思考一会,小试牛刀!!
1 | var test = { |
介绍
遍历器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就阔以完成遍历操作;
模拟next方法返回值的例子
1 | var it = makeIterator(['a', 'b']) |
默认 Iterator 接口
Iterator 接口的目的,就是为所有数据结构,提供了一种统一的访问机制,即for…of循环。当使用for…of循环遍历某种数据结构时,该循环会自动去寻找 Iterator 接口。
一种数据结构只要部署了 Iterator 接口,我们就称这种数据结构是“可遍历的”(iterable)。
ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是可遍历的(iterable)。
Symbol.iterator本身是一个函数,数据结构默认的遍历器生成函数,执行这个函数就会返回一个遍历器。
原生具备 Iterator 接口的数据结构如下:
Array
Set
Arguments
Map
String
NodeList对象
typedArray
对于类似数组的对象(存在数值键名和length属性),部署 Iterator 接口,有一个简便方法,就是Symbol.iterator方法直接引用数组的 Iterator 接口。
1
2
3
4
5var arr = new Set([1, 2, 3, 2, 3])
let it = arr[Symbol.iterator](); // 得到遍历器对象
it.next()
it.next()
it.next()变量arr是一个数组,原生就具有遍历器接口,部署在arr的Symbol.iterator属性上面。所以,调用这个属性,就得到遍历器对象。
类似数组的对象调用数组的Symbol.iterator方法的例子
1 | var iterable = { |
若没有部署[].Symbol.iterator则会报错 iterable is not iterable
普通对象部署数组的Symbol.iterator方法无效
1 | var iterable = { |
手动部署iterator属性的时候记得是 [Symbol.iterator]: [][Symbol.iterator] 这种写法
手写实现一个遍历器
目的: 定义一个对象,通过调用类Iterator实现可遍历
1 | let obj = { |
如何调用类Iterator实现可遍历
1 | class MyIterator { |
场景
有一些场合会默认调用 Iterator 接口(即Symbol.iterator方法),除了下文会介绍的for…of循环,还有其他场合
解构赋值
1 | var set = new Set().add('a').add('b').add('c') |
扩展运算符
1 | // 例二 |
上面代码的扩展运算符内部就调用 Iterator 接口。
只要某个数据结构部署了 Iterator 接口,就可以对它使用扩展运算符,将其转为数组。
yield*
yield*后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。
1 | var ge = function* () { |
思考 如何让第二个next里的数据遍历呢?
1 | var ge = function* () { |
其他场景
由于数组的遍历回调用遍历器接口,所以任何接受数组作为参数的场景都会调用遍历器
- for…of
- Array.from()
- Map(), Set(), WeakMap(), WeakSet()(比如new Map([[‘a’,1],[‘b’,2]]))
- Promise.all()
- Promise.race()
字符串的 Iterator 接口
字符串是一个类似数组的对象,也原生具有 Iterator 接口
1 | var someString = "hi"; |
上面代码中 调用Symbol.iterator方法返回一个遍历器对象,在这个遍历器上可以调用 next 方法,实现对于字符串的遍历
Iterator与Generator
Symbol.iterator()方法的最简单实现, 两者关系
任意一个对象的Symbol.iterator方法 === 该对象的遍历器生成函数Generator,调用该函数会生成该对象的遍历器对象.
Generator函数就是遍历器生成函数,因此能把Generator赋值给对象的Symbol.iterator属性,从而使该对象具有iterator接口
- 第一种
1
2
3
4
5
6
7
8var it = {
[Symbol.iterator]: function* () {
yield 1;
yield 2;
yield 3;
}
}
console.log([...it]) // [1, 2, 3] - 第二种
1
2
3
4
5
6
7
8var it2 = {
* [Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
}
}
console.log(...it2) // 1 2 3
for…of 循环
一个结构只要部署了Symbol.iterator属性,就被视为具体Iterator接口,就能用for of 循环遍历成员,也就是for of 循环内部调用的是数据结构的Symbol.iterator属性
for…of循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如arguments对象、DOM NodeList 对象)、Generator 对象,以及字符串
思考
1.所有的模拟数据结构,用到的Symbol.iterator ,再用的时候都需要加上方括号,不管是
1 | var it2 = { |
2.面试的时候如何用大白话说清楚,Iterator跟Generator俩者之间的关系