迭代器
理解迭代。
什么是可迭代iterable?
我们都知道数组是可迭代的, 对象是不可迭代的(有人可能会说for-in可以啊)。那是根据什么判断的呢?
判定依据就是是否实现了iterable接口。 可迭代对象都有iterable这个接口,要查看它的迭代器,要访问[Symbol.iterator],这个键就是要用计算属性访问.
let itr = '12321' console.log(itr[Symbol.iterator]) // ƒ [Symbol.iterator]() { [native code] }
可迭代有什么特征
for-of 循环
数组解构
扩展操作符
Array.from()
创建集合
创建映射
Promise.all()接收由期约组成的可迭代对象
Promise.race()接收由期约组成的可迭代对象
yield*操作符,在生成器中使用
这些原生语言结构会在后台调用提供的可迭代对象的这个工厂函数,从而创建一个迭代器.
我们常用的也就是 for-of 循环、 数组解构 、扩展操作符。有了前面几个t特性,Array.from()把迭代对象转为数组就不用了。顺便提一下 Array.of()方法,这个方法的效果等于[], 意思就是Array.of(...items) 的返回值就是 [...items]。
这里说的有点问题,更正一下,如果给from方法传一个可迭代对象,那就如同前面所说。详见from的用法
这里就有一个字符串转数组的骚操作, 适用于任何可迭代对象转数组,因为字符串也是可迭代对象。
[...'dskjtrdkytj']
// ['d', 's', 'k', 'j', 't', 'r', 'd', 'k', 'y', 't', 'j']
除了Array String 之外 Map Set 也是可迭代对象,这也是为什么可以用
[...new Set(arr)]
给数组去重的原因.
生成器
什么是生成器? 这个问题本人也不太理解, 还得从生成器函数说起。
从语法上说 生成器 Generator 函数也就比普通函数多了一个*,从作用上来说生成器是用来自定义可迭代对象的。
调用生成器函数会得到一个 生成器对象 生成器对象是可迭代的, 也可以调用next()进行消费,生成器的迭代器默认指向自身。
生成器函数内部可使用 yield关键字 中断和开始执行,yield关键字后面的值 ,会出现在next()返回的对象的value属性里。
同一函数的生成器实例之间是互不影响的。也就是每次调用生成器函数,会返回一个新的生成器。
所有的生成器对象都有return方法 一旦关闭就无法恢复。
重点说一下yeild关键字
yeild关键字的作用就是把执行权交出去。 本来我们js的代码是一行一行执行的,但是碰到了yeild之后, 下一行代码将不会立即执行, yeild所在的这一行代码,还是正常执行。
因为yeild只能出现在生成器函数中, 所以碰到yeild必然是调用了生成器函数。 所谓交出执行权,就是中断这个函数的执行。
不同于return的是,后面还能要回执行权继续执行。 调用生成器对象的next方法即可恢复函数的执行。 yeild 中断时会把 其后表达式的返回值作为next方法的返回对象的value。
注意,生成器函数的逻辑并不会在调用生成器之后就立即执行,而是需要调用一个next()才会执行。
yeild的返回值
默认是没有也就是 undefined。 它的返回值是下一次调用next() 时,传入的参数。 看上去有点矛盾,因为这一行代码已经执行了,但是却没有返回值,要等再次调用next()传参决定其返回值。 我们可以这么理解,yeild返回值就是undefined, 调用next()方法,会修改这个值。
function *generation(){
yield 1
yield 2
}
/*生成器默认是自引用的*/
let iter2 = generation()
console.log(iter2[Symbol.iterator]() === iter2)
console.log(iter.next())
function *nTimes(n){
let i = 0;
while(n--){
yield i++
}
}
function *range (start, end){
let i= 0;
while(start < end){
yield i++ ;
}
}
/* 生成器也可以作为对象属性 和静态类属性*/
let genar12 = {
* count(n){
yield n
while(n--){
yield n
}
}
}
let arr3 = genar12.count(10)
for(let n of arr3){
console.log(n);
}
await的前身, 大概了解一下就行
let ps = new Array(4).fill().map( i => new Promise())
function fna(arr){
for(num of arr){
yield num
}
}
function co (p = new Promise()){
let fn = fna() ;
next()
function next(data){
let result = fn.next()
if(!result.done){
result.value.then((info)=> {
next(info)
})
}
}
}
用于递归
据红宝书描述 生成器最大的作用是递归, 递归的过程中不断消费生成器。等我理解了再来写
自定义迭代接口
一个对象如果有Symbol.iterator方法就是可迭代对象。最简单的办法就是直接给对象添加这个方法,更通用的办法是用生成器来实现迭代接口。 three.js里的向量对象就是这么做的
* [Symbol.iterator]() {
yield* this.x;
yield* this.y;
yield* this.z;
}