忽略的知识点 - 迭代器及迭代器协议
迭代器是我们我们平时接触很多的一个特性
当我们遍历数组、字符串或者使用解构赋值、对象展开运算符时都会用到迭代器协议相关内容
1. 什么是可迭代协议和迭代器协议
① 可迭代协议
顾名思义,就是让对象支持被迭代的协议
当我们执行以下代码时,可以明确知道什么值可以被循环得到
let a = [1,2,3] for(let i of a) { console.log(i) // 1,2,3 }
如何支持可迭代协议呢?
对象(或者它原型链上的某个对象)必须有一个名字是 Symbol
.iterator
的属性
Symbol.iterator : 提供一个无参函数, 返回的对象实现迭代器协议
② 迭代器协议
迭代器协议定义了如何产生和返回迭代器函数的结果
实现迭代器协议的对象至少应该包含一个next方法
next方法:
提供一个对象的无参函数,返回的对象拥有两个属性:
done: boolean 不提供则默认是false
value: 用于迭代时获取的对象,
2. 验证现有实现可迭代协议对象是否具备上述的属性
验证迭代对象中的next方法
let a = [1,2,3] var aIt = a[Symbol.iterator]() aIt.next() //{value: 1, done: false} aIt.next() //{value: 2, done: false} aIt.next() //{value: 3, done: false} aIt.next() //{value: undefined, done: true}
next方法用于依次返回用于迭代的对象,咋一看,这不就是 generator的next方法吗
3. 如何对自定义对象实现迭代器协议
a. 通过扩展对象,实现协议定义的方法和对象
var c={a:1,1:2,c:4,b:5} for(let i of c) { console.log(c) } //Uncaught TypeError: c is not iterable at <anonymous>:2:14
接下里我们按照上述协议内容将c对象扩展成支持for...of遍历, 首先实现可迭代协议 即为对象添加Symbol.iterator属性
c[Symbol.iterator] = function(){ return {} }
如何实现迭代器协议, 即在return中返回包含next方法的对象
c[Symbol.iterator] = function(){ return { _i:0, next() { let keys = Object.keys(c) if(this._i<keys.length){ console.log("调用iterator-next:"+i) return {value: keys[this._i++],done: false} }else { return {done: true, value:undefined} } } } }
测试调用
for(let i of c){console.log(i)} //1 a c b
注: 以上实现中 next方法 this对象指向的是next返回对象
b. 利用generator对象
var c={a:1,1:2,c:4,b:5} function* ci() { let keys = Object.keys(c),i=0 while(i<keys.length){ yield keys[i++] } } c[Symbol.iterator] = ci
调用测试
for(let i of c){console.log(i)} // 1 a c b
================
为什么可以使用generator实现
1.(MDN)调用一个生成器函数并不会马上执行它里面的语句,而是返回一个这个生成器的 迭代器 (iterator )对象。当这个迭代器的 next() 方法被首次(后续)调用时,其内的语句会执行到第一个(后续)出现yield的位置为止,yield 后紧跟迭代器要返回的值。或者如果用的是 yield*(多了个星号),则表示将执行权移交给另一个生成器函数(当前生成器暂停执行)
2. cig = new ci()// generator对象。 cig[Symbol.iterator]().next === cig.next
=================
c. 在对象原型链上实现通用的迭代器协议
Object.prototype[Symbol.iterator] = function(){ var i=0; return {next:() =>{ var keys = Object.keys(this) if(i< keys.length){ return {value: keys[i++],done:false} }else{ return {done: true} } }, } }
测试
var a = {b:1}for(let i of a){console.log(i)}
// bfor(let i in a){console.log(i)}
// b
3. 实现可迭代协议有什么作用
①. es6中的for...of语法就是基于迭代协议的
let a = [1,2,3] for(let i of a) { console.log(i) } //1 //2 //3
②. 对象展开符
var b = {a:1,1:2,c:4,b:5} [...b] // Uncaught TypeError: object is not iterable (cannot read property Symbol(Symbol.iterator))
而对上面我们修改的对象C执行以下代码
[...c] // ["1", "a", "c", "b"]
③. yield* 表达式
function* a(){ yield* c } var ac = a() ac.next() //{value: "1", done: false} ac.next() //{value: "a", done: false} ac.next() //{value: "c", done: false} ac.next() //{value: "b", done: false} ac.next() //{value: undefined, done: true}
④. 解构赋值
借助对象C的console语句观察解构赋值的过程
[a1,b1] = c // 调用iterator-next:0 // 调用iterator-next:1 // a1: 1 b1: a
以上就是迭代协议相关内容及运用
发现了Symbol.iterator属性的神奇之处,忍不住又看看了Symbol其他的数据, 发现又一个叫asyncIterator的属性
参考iterator, 那就是定义了一个异步可迭代对象了吧 ~_~
4. asyncIterator属性作用
用于被for await...of 语句循环使用 (注: for await...of 可用于遍历同步或异步实现迭代器协议的对象)
①. 实现方式
同iterator,需要含义Symbol.asyncIterator属性, 提供返回一个无参函数,返回一个包含next方法的对象
②. 举个栗子
暂时可能没有想到什么必须合适的场景,那就无病呻吟一下吧,↓
先验证下for await...of
var a = [Promise.resove(1),Promise.resolve(2),Promise.resolve(3)]for(let i of a){
console.log(i)
}
// promise * 3for await(let i of a) {
console.log(i)
}
// 1,2,3
使用异步生成器构造一个异步可迭代对象
var asyncIterable = { [Symbol.asyncIterator]() { return { i: 0, next() { if (this.i < 3) { return Promise.resolve({ value: this.i++, done: false }); }return Promise.resolve({ done: true }); } };
}
};for await (let num of asyncIterable) {
console.log(num);
}
// 0,1,2
③,现状
目前还木有实现了asyncIterator内置属性的对象
5. Iterator对象
注: 非标准属性,只在FF下有实现,慎用
Iterator
函数返回一个对象,它实现了遗留的迭代协议,并且迭代了一个对象的可枚举属性。
var a = {a: 1,1: 2,c:3,b:4};for (var [name, value] of Iterator(a)) {
console.log(name, value);
}
// 1,2 a,1 c,3 b,4
标题:忽略的知识点 - 迭代器及迭代器协议
作者:hugh0524
地址:https://blog.uproject.cn/articles/2019/08/09/1565324134546.html
0 0