关注[前端小讴],阅读更多原创技术文章
迭代方法 | 优/缺点 |
---|---|
for |
① 需知道如何使用数据结构 ② 遍历顺序不是固有的 |
forEach() |
① 无法标识迭代终止 ② 只适用数组 ③ 回调结构较笨拙 |
Iterator 接口 |
无需了解可迭代对象的结构,只需知道如何取得连续的值 |
相关代码 →
// 未实现迭代器工厂函数
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 }
class FooArray extends Array {
}
let fooArr = new FooArray('foo', 'bar', 'baz')
for (let el of fooArr) {
console.log(el)
/*
foo
bar
baz
*/
}
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 }
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
*/
}
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
*/
}