什么是可迭代对象?
数组或集合(set)这样的集合类型的对象,是可迭代对象,但是,可迭代对象不一定是集合对象。
有哪些实现iterator接口的数据类型?
Array
、String
、Map
、Set
、arguments
、nodeList等dom集合模型
Symbol.iterator
是啥?
通过访问Symbol.iterator
,可以知道内置的迭代
- 实现iterator接口的数据类型,都有Symbol.iterator属性
- Symbol.iterator属性引用的是迭代器的工厂函数
- 调用这个工厂函数,返回一个新迭代器
看下面的例子:
const arr = [1,2,3]
const m = new Map().set(1,'a').set(2,'b')
console.log(arr[Symbol.iterator]) // Symbol.iterator属性在数组中指代的是values属性
console.log(m[Symbol.iterator]()) // Symbol.iterator属性引用的是迭代器的工厂函数
console.log(arr[Symbol.iterator] === arr.values)
for(let i of m.entries()){
console.log(i);
}
对应的执行结果:
- 可以用Symbol.iterator属性来检查某类型的变量是否实现了迭代器工厂函数
如果没有实现时,Symbol.iterator
属性是undefined
const flag = true
const num = 1
const str = new Set().add(2).add(3)
console.log(flag[Symbol.iterator]); // undefined
console.log(num[Symbol.iterator]); // undefined
console.log(str[Symbol.iterator]); // ƒ values() { [native code] }
- 原型链的父类实现了iterator接口,子类也会实现
class childArray extends Array {}
const inst = new childArray('a', 'b', 1, 5)
for(let i of inst){
console.log(i); //依次输出: 'a' 'b' 1 5
}
迭代器是什么?
迭代器是一次性使用的对象,用于迭代与它相关的可迭代对象(数组、set,map 等)
迭代器怎么使用?
- 迭代器用next()方法,对可迭代对象进行遍历。
- 调用next( )方法后,返回的是对象,格式类似这种: {done: false, value: 'hello'}
迭代器有什么特性?
1,调用它的next()方法遍历它对应的可迭代对象时,一开始resultObj的done是false,迭代完毕后done是true
2,done为true后,继续调用next(),返回的resultObj不变
3,不同迭代器实例之间没有影响
4,某次迭代后,可迭代对象改了(例如插入了某个值),继续迭代后,迭代的是修改后的可迭代对象
5,迭代器工厂函数(iterableObj[Symbol.iterable]
) 执行后,得到迭代器,迭代器也实现了iterator
接口,且执行迭代器工厂函数后,返回的是相同的迭代器。看例子
const arr = [3,4,5,6]
const iter = arr[Symbol.iterator]()
console.log(iter === iter[Symbol.iterator]());
如何提前终止迭代器?
总的来说,就是调用迭代器的return方法。比如:
1,for...of
的break
,continue
,return
或者 throw
2,解构操作,并未消费所有值
(可参考书中的例子进行理解)
终止迭代器有哪些注意的点?
1,如果迭代器没关闭,会从上次离开的地方,继续迭代
2, 迭代器有return方法时,都会被调用
2,有些迭代器是不可关闭的,但是,给迭代器加了return函数之后,终止迭代器,return会被调用,但是不可关闭属性还是不变。看例子:
const arr = [1,3,5,6]
const iter = arr[Symbol.iterator]()
console.log(iter.return) // undefined
iter.return = function () {
console.log("this is the end")
return {done: true, value: "xx"} // return返回的数据,必须含done:true
}
for(let i of iter){
console.log('i:',i);
if(i > 4){
break;
}
}
for(let i of iter){
console.log('i==',i); // 即便给return属性赋值,iter的不可关闭属性还是不变,此处还是继续上面的for...of迭代
}
迭代器有什么作用?
1,迭代器可以遍历对应的迭代对象。在实际代码中,迭代器的作用是隐性的。比如下面语法会自动调用迭代器的工厂函数,创建迭代器 :
for...of
数组解构
扩展运算符
Array.from()
创建集合
创建映射
Promise.all()
Promise.race()
yield* 操作符
2,以上操作会对实现了iterator
接口的数据类型,执行: 创建迭代器 -> 调用迭代器next()方法 -> 取resultObj中value属性的值。,
3,迭代器是某些操作的底层原理,比如 for..of
循环之所以能够遍历实现了iterator接口的数据,是因为 for...of
操作自动创建了迭代器
生成器是什么?
生成器是一种函数,具体来说就是function后面加了星号*的函数。
定义函数的地方都能定义生成器(函数),箭头函数除外
生成器怎么使用?
1,生成器(函数)中,function后的*,不受两侧空格影响,
2,调用生成器(函数),会返回一个生成器对象,调用生成器对象的next() 方法,生成器(函数)才会开始执行
3,生成器对象next() 方法的返回值和迭代器调用next( )方法返回值一样,resultObj含done和value属性
4,生成器对象也实现了iterator接口,默认迭代器是自引用
const generatorFn = function * () {
return "xixi"
}
const gen = generatorFn()
console.log(gen.next()) // { value: 'xixi', done: true }
console.log(gen === gen[Symbol.iterator]()) // true
生成器的yield关键字,有什么作用?
yield关键字可以让生成器停止和开始执行,yield只能在生成器函数内部使用。
调用生成器对象的next() 方法,生成器函数会执行到yield关键字这一行,然后暂停
const generatorFn = function *() {
yield "first"
yield "two"
return "three"
}
const gen = generatorFn()
console.log(gen.next()); // { value: 'first', done: false }
console.log(gen.next()); // { value: 'two', done: false }
console.log(gen.next()); // { value: 'three', done: true }
yield结合生成器函数有哪些使用场景?
1,由于 生成器对象也实现了iterator接口, 所有 可以用for...of, Array.from( ) 等,来遍历生成器对象
const generatorFn = function *() {
yield 'first'
yield 'two'
yield 'three3'
}
const gen = generatorFn()
console.log(Array.from(gen)); // [ 'first', 'two', 'three3' ]
2,使用yield实现输入和输出
两种情况:
- next传值,赋给yield
- yield后面的值,被抛出来
const generatorFn = function *(init) {
let a = yield 3
console.log('a:', a)
let c = yield 4
console.log('c:', c)
let b = yield 8 * init
console.log(b + 1);
return b
}
const gen = generatorFn(3)
console.log(gen.next(1)); // { value: 3, done: false }
console.log(gen.next(2)); // a: 2, { value: 4, done: false }
console.log(gen.next('a==**')); // c: a==** , { value: 24, done: false }
console.log(gen.next(69)); // 70 , { value: 69, done: true }
以上例子说明:
- 调用next(),得到的resultObj中value值是yield后面的表达式
- 第一次调用next的传参不生效,gen.next(1),传入的1没有生效
- 从第二次起,当next有传参,传参会作为上一行yield等式左边的变量的值
- 总结下:yield后面表达式的值,会作为resultObj的value抛出;next(param) 传入的param,会赋值给上一个yield表达式左侧的变量,然后参与generatorFn函数接下来的计算
yield * 生成可迭代对象的用法?
- yield 后面 * 两侧的空格不受影响
- yield * 是将可迭代对象,单独序列化为一连串可单独产出的值
const generatorFn = function *() {
yield * [3,2,5]
}
for(let i of generatorFn()) {
console.log('value: ', i) // 3,2,5
}
从上面例子看出,可把yield *[3,2,5]
理解成 yield 3, yield 2, yield 5, 也就是yield * 将[3, 2, 5] 做了自动展开
生成器有什么作用?
生成器作为默认迭代器。
结合yield * ,实现递归操作。
function * nTimes(n) {
if(n >0) {
yield * nTimes(n -1)
yield n - 1
}
}
for(const x of nTimes(4)) {
console.log(x)
}
如何提前终止生成器?
1,生成器对象有return方法,终止了之后,后面再调用next方法,将会失效。
const generatorFn = function *() {
for(const x of [1,3,5]){
yield x
}
}
const g = generatorFn()
console.log(g.next()); // { value: 1, done: false }
console.log(g.next()); // { value: 3, done: false }
console.log(g.return()); // { value: undefined, done: true }
console.log(g.next()); // { value: undefined, done: true }
2,生成器对象的throw方法。将提示的错误注入到生成器对象,如果错误未被处理,则生成器关闭