关注前端小讴,浏览更多原创技术文章
迭代器模式
- ES6 新增了 2 个高级个性:迭代器 和生成器
迭代办法 | 优 / 毛病 |
---|---|
for |
① 需晓得如何应用数据结构 ② 遍历程序不是固有的 |
forEach() |
① 无奈标识迭代终止 ② 只实用数组 ③ 回调构造较蠢笨 |
Iterator 接口 |
无需理解可迭代对象的构造,只需晓得如何获得间断的值 |
相干代码 →
可迭代协定
- 实现 Iterable 接口要求同时具备:① 反对迭代的自我辨认 ② 创立实现 Iterator 接口的对象
- 必须裸露一个属性作为 默认迭代器 ,属性应用Symbol.iterator 作为键
- 默认迭代属性必须援用一个 迭代器工厂函数 ,调用工厂函数返回一个 新迭代器
- 实现了 Iterable 接口的内置类型:字符串、数组、映射、汇合、arguments 对象、NodeList 等 DOM 汇合类型
// 未实现迭代器工厂函数
let num = 1
let obj = {}
console.log(num[Symbol.iterator]) // undefined
console.log(obj[Symbol.iterator]) // undefined
// 实现了迭代器工厂函数
let str = 'abc'
let arr = ['a', 'b', 'c']
let map = new Map().set('a', 1).set('b', 2)
let set = new Set().add('a').add('b')
console.log(str[Symbol.iterator]) // ƒ [Symbol.iterator]() { [native code] }
console.log(arr[Symbol.iterator]) // ƒ values() { [native code] }
console.log(map[Symbol.iterator]) // ƒ entries() { [native code] }
console.log(set[Symbol.iterator]) // ƒ values() { [native code] }
// 调用迭代器工厂函数,生成新的迭代器
console.log(str[Symbol.iterator]()) // StringIterator {}
console.log(arr[Symbol.iterator]()) // ArrayIterator {}
console.log(map[Symbol.iterator]()) // MapIterator {"a" => 1, "b" => 2}
console.log(set[Symbol.iterator]()) // StringIterator {"a", "b"}
- 不须要显示调用迭代器工厂函数,实现可迭代协定的 所有类型 都主动兼容承受 可迭代对象的任何语言个性
-
承受可迭代对象的原生语言个性 包含:
for-of
循环- 数组解构
- 扩大操作符
Array.from()
- 创立汇合
- 创立映射
Promise.all()
接管由期约组成的可迭代对象Promise.race()
接管由期约组成的可迭代对象yield*
操作符,在生成器中应用
for (let el of arr) {console.log(el) // for-of 循环
/*
'a'
'b'
'c'
*/
}
let [a, b, c] = arr // 数组解构
console.log(a, b, c) // 'a' 'b' 'c'
let arr2 = [...arr] // 扩大操作符
console.log(arr2) // ['a', 'b', 'c']
let arr3 = Array.from(arr) // Array.from()
console.log(arr3) // ['a', 'b', 'c']
let set2 = new Set(arr)
console.log(set2) // Set(3) {'a', 'b', 'c'}
let pairs = arr.map((x, i) => [x, i])
console.log(pairs) // [[ 'a', 0], ['b', 1], ['c', 2] ]
let map2 = new Map(pairs)
console.log(map2) // Map(3) {'a' => 0, 'b' => 1, 'c' => 2}
- 如果对象原型链上的 父类实现了 Iterabe 接口,该对象也实现了这个接口
class FooArray extends Array {}
let fooArr = new FooArray('foo', 'bar', 'baz')
for (let el of fooArr) {console.log(el)
/*
foo
bar
baz
*/
}
迭代器协定
-
迭代器 API 应用
next()
办法在可迭代对象中遍历数据,每次胜利调用next()
都返回一个IteratorResult
对象:IteratorResult
蕴含 2 个属性 done 和valuedone
是一个布尔值,false 为未耗尽,true 为耗尽value
蕴含可迭代对象的下一个值(若 done 为 true 则 value 为 undefined)- 迭代器达到
done:true
状态,后续再调用next()
始终返回{done:true,value:undefined}
let arr4 = ['foo', 'bar'] // 可迭代对象
console.log(arr4[Symbol.iterator]) // ƒ values() { [native code] },迭代器工厂函数
let iter = arr4[Symbol.iterator]() // 迭代器
console.log(iter) // ArrayIterator {}
console.log(iter.next()) // {value: 'foo', done: false},执行迭代
console.log(iter.next()) // {value: 'foo', done: false},执行迭代
console.log(iter.next()) // {value: undefined, done: true},执行迭代
console.log(iter.next()) // {value: undefined, done: true},执行迭代
- 同一个可迭代对象的 不同迭代器 实例之间 没有分割 ,每个迭代器都 独立 地遍历可迭代对象
let iter2 = arr4[Symbol.iterator]() // 迭代器 iter2,迭代可迭代对象 arr4
let iter3 = arr4[Symbol.iterator]() // 迭代器 iter2,迭代可迭代对象 arr4
console.log(iter2.next()) // {value: 'foo', done: false}
console.log(iter3.next()) // {value: 'foo', done: false}
console.log(iter3.next()) // {value: 'bar', done: false}
console.log(iter2.next()) // {value: 'bar', done: false}
- 可迭代对象在迭代期间被批改,迭代器反映相应变动;迭代器保护的是 指向可迭代对象的援用 ,会 阻止垃圾回收程序 回收可迭代对象
let arr5 = ['foo', 'baz']
let iter4 = arr5[Symbol.iterator]()
console.log(iter4.next()) // {value: 'foo', done: false}
arr5.splice(1, 0, 'bar') // 迭代期间,可迭代对象被批改
console.log(iter4.next()) // {value: 'bar', done: false}
console.log(iter4.next()) // {value: 'baz', done: false}
console.log(iter4.next()) // {value: undefined, done: true}
自定义迭代器
- 任何 实现了 Iterable 接口(具备 Symbol.iterator 属性)的对象都能够作为迭代器应用
class Counter {
// 构造函数
constructor(limit) {
this.count = 1
this.limit = limit
}
// Iterable 接口,实现自定义迭代
[Symbol.iterator]() {return this}
// 原型上的迭代办法
next() {if (this.count <= this.limit) {return { value: this.count++, done: false}
} else {return { value: undefined, done: true}
}
}
}
let counter = new Counter(5)
console.log(counter) // Counter {count: 1, limit: 5},构造函数
console.log(counter[Symbol.iterator]) // ƒ [Symbol.iterator]() { return this},迭代器工厂函数
for (let i of counter) {console.log(i)
/* 实现了自定义迭代器:1
2
3
4
5
*/
}
for (let i of counter) {console.log(i)
/* 只能迭代 1 次
nothing logged
*/
}
- 将计数器变量放到闭包里,实现同一个 可迭代对象 可能 创立多个迭代器
class Counter2 {constructor(limit) {this.limit = limit}
[Symbol.iterator]() {
let count = 1 // 计数器变量放到闭包中
let limit = this.limit
return {next() {if (count <= limit) {return { value: count++, done: false}
} else {return { value: undefined, done: true}
}
},
}
}
}
let counter2 = new Counter2(3)
for (let i of counter2) {console.log(i)
/*
1
2
3
*/
}
for (let i of counter2) {console.log(i)
/* 同一个可迭代对象,可能创立多个迭代器:1
2
3
*/
}
- 任何迭代器,调用 Symbol.iterator 属性援用的工厂函数后,返回与原迭代器雷同的迭代器
let arr6 = ['foo', 'bar', 'baz']
let iter5 = arr6[Symbol.iterator]()
console.log(iter5) // ArrayIterator {}
let iter6 = iter5[Symbol.iterator]() // 迭代器再次调用工厂函数,生成新的迭代器
console.log(iter6) // // ArrayIterator {}
console.log(iter5 === iter6) // true
let iter7 = iter6[Symbol.iterator]() // 迭代器再次调用工厂函数,生成新的迭代器
console.log(iter7) // // ArrayIterator {}
console.log(iter6 === iter7) // true
for (let i of iter7) {console.log(i)
/*
'foo'
'bar'
'baz'
*/
}
提前终止迭代器
-
可选的
return()
办法可在迭代器未耗尽时,指定迭代器提前敞开时执行的逻辑,迭代器 提前敞开的可能状况 包含:for-of
循环通过break
、continue
、return
或throw
提前退出解构操作
未生产所有值
class Counter3 {constructor(limit) {this.limit = limit}
[Symbol.iterator]() {
let count = 1 // 计数器变量放到闭包中
let limit = this.limit
return {next() {if (count <= limit) {return { value: count++, done: false}
} else {return { value: undefined, done: true}
}
},
// 提前终止迭代器的办法
return() {console.log('Exiting early')
return {done: true}
},
}
}
}
let counter3 = new Counter3(5)
for (let i of counter3) {if (i > 2) {break // 提前终止迭代器,调用迭代器的 return()办法
}
console.log(i)
/*
1
2
'Exiting early'
*/
}
try {for (let i of counter3) {if (i > 2) {throw 'err' // 提前终止迭代器,调用迭代器的 return()办法
}
console.log(i)
/*
1
2
'Exiting early'
*/
}
} catch (e) {}
let [a2, b2, c2, d2, e2, f2] = counter3 // 解构操作,生产所有值
console.log(a2, b2, c2, d2, e2, f2) // 1 2 3 4 5 undefined
let [a3, b3, c3] = counter3 // 'Exiting early',解构操作,未生产所有值
- 若迭代器未敞开,提前终止后能够从上次来到的中央持续迭代,数组的迭代器就是不能敞开的
let arr7 = [1, 2, 3, 4, 5]
let iter8 = arr7[Symbol.iterator]()
for (let i of iter8) {console.log(i)
if (i > 2) {break // 提前退出迭代器,但不敞开}
/*
1
2
3
*/
}
for (let i of iter8) {console.log(i)
/* 持续迭代
4
5
*/
}
-
迭代器实例的
return
属性 是否为函数对象 ,决定迭代器 是否可敞开- 给不可敞开的迭代器减少
return()
办法 不能 让该迭代器变得可敞开,但提前终止会调用新增的return()
办法
- 给不可敞开的迭代器减少
let arr8 = [1, 2, 3, 4, 5]
let iter9 = arr8[Symbol.iterator]()
console.log(iter9.return) // undefined,迭代器不可敞开
iter9.return = function () {
// 追加 return 办法,但无奈让迭代器变得可敞开
console.log('Exiting early')
return {done: true}
}
for (let i of iter9) {console.log(i)
if (i > 2) {break // 提前退出迭代器}
/*
1
2
3
'Exiting early',提前终止迭代器仍会调用 return()办法
*/
}
for (let i of iter9) {console.log(i)
/* 持续迭代
4
5
*/
}
总结 & 问点
- 实现迭代有哪些形式?其优、毛病别离是什么?默认迭代器必须应用什么属性作为键?
- 如何检测数据类型是否实现了迭代器工厂函数?调用这个工厂函数会生成什么?
- 迭代器 API 调用哪个办法遍历数据?其返回值是什么?同一个可迭代对象的不同迭代器实例之间有分割么?
- 迭代器真正保护的是什么?其对垃圾回收程序有什么影响?可迭代对象在迭代期间被批改,迭代器受影响么?
- 写一段代码创立一个自定义迭代器,实现“同一个可迭代对象可能创立多个迭代器”
- 迭代器调用 Symbol.iterator 属性援用的工厂函数会生成什么?
- 如何指定迭代器提前敞开时执行的逻辑?哪些状况下迭代器会提前终止?
- 提前终止的迭代器若未敞开,是否可持续迭代?如何判断迭代器是否可敞开?
- 不可敞开的迭代器是否变成可敞开的?请用代码证实