
迭代就是指可以從一個資料集中依照一定的順序,不斷取出資料的過程。
那麼迭代和遍歷有啥子差異呢?
在JavaScript中,迭代器是能呼叫next方法實作迭代的一個對象,該方法傳回一個具有兩個屬性的物件。
value :可迭代物件的下一個值done :表示是否已經取出所有的資料了。 false表示還有數據, true表示後面已經沒有數據了。透過可迭代物件中的迭代器工廠函數Symbol.iterator來產生迭代器。
const arr = []console.log(arr)

const arr = [1, 2, 3]
const iter1 = arr[Symbol.iterator]() // 透過迭代器工廠函數` Symbol.iterator`來產生迭代器。
console.log(iter1)
console.log(iter1.next())
console.log(iter1.next())
console.log(iter1.next())
console.log(iter1.next())
console.log('%c%s', 'color:red;font-size:24px;', '================')
const mymap = new Map()
mymap.set('name', 'clz')
mymap.set('age', 21)
const iter2 = mymap[Symbol.iterator]() // 透過迭代器工廠函數` Symbol.iterator`來產生迭代器。
console.log(iter2)
console.log(iter2.next())
console.log(iter2.next())
console.log(iter2.next())
可以發現,迭代器是取完最後一個值之後,也就是迭代器下一個值value為undefined時,完成。
但是,上面的說法並不是很準確,並不是迭代器下一個值value為undefined時,就完成的。還需要判斷是不是真的沒有值,還是是可迭代物件裡就有一個值為undefined 。如果是可迭代物件裡有一個值為undefined的情況,那麼此時還是不會變成完成狀態。
const arr = [1, 2, 3, undefined] const iter1 = arr[Symbol.iterator]() // 透過迭代器工廠函數` Symbol.iterator`來產生迭代器。 console.log(iter1) console.log(iter1.next()) console.log(iter1.next()) console.log(iter1.next()) console.log(iter1.next()) console.log(iter1.next())

可以多次呼叫迭代器工廠函數來產生多個迭代器,每個迭代器都表示可迭代物件的一次性有序遍歷。不同迭代器之間互不干擾,只會獨立遍歷可迭代物件。
const arr = [1, 2, 3]
const iter1 = arr[Symbol.iterator]() // 透過迭代器工廠函數` Symbol.iterator`來產生迭代器。
const iter2 = arr[Symbol.iterator]()
console.log('迭代器1:', iter1.next())
console.log('迭代器2:', iter2.next())
console.log('迭代器1:', iter1.next())
console.log('迭代器2:', iter2.next())
const arr = [1, 2, 3]
const iter = arr[Symbol.iterator]()
for (const i of iter) {
console.log(i) // 依序輸出1、2、3
}如果可迭代物件在迭代期間被修改了,迭代器得到的結果也會是修改後的。
const arr = [1, 2, 3] console.log(arr) const iter = arr[Symbol.iterator]() console.log(iter.next()) arr[1] = 999 console.log(iter.next()) console.log(iter.next())

當我們迭代到done: true之後,再呼叫next是不是會報錯,或是不回傳任何內容呢?
然而,並不是,迭代器會處於一種完成但並不完成的狀態, done: true表示已經完成了,但是後續還能一直調用next ,雖然得到的結果一直都會是{ value: undefined, done: true } 。這就是為什麼說完成但不完成。
const arr = [1, 2, 3] const iter = arr[Symbol.iterator]() console.log(iter.next()) console.log(iter.next()) console.log(iter.next()) console.log(iter.next()) console.log(iter.next()) console.log(iter.next())

從上面的例子中,我們可以知道是透過透過迭代器工廠函數Symbol.iterator來產生迭代器,所以我們需要實作一個迭代器迭代器工廠函數,然後迭代器可以呼叫next方法,所以還需要實作一個next方法,至於迭代器工廠函數,實際上直接傳回實例this 。
計數器範例:
class Counter {
constructor(limit) {
this.count = 1
this.limit = limit }
next() {
if (this.count <= this.limit) {
return {
done: false,
value: this.count++
}
} else {
return { done: true, value: undefined }
}
}
[Symbol.iterator]() {
return this
}} const counter = new Counter(3) const iter = counter[Symbol.iterator]() console.log(iter.next()) console.log(iter.next()) console.log(iter.next()) console.log(iter.next()) console.log(iter.next())

乍一看,沒啥問題,但是如果我們使用for-of來遍歷就能發現問題。
const counter = new Counter(3)for (let i of counter) {
console.log(i)}console.log('另一輪迭代:')for (let i of counter) {
console.log(i)}
使用for-of循環也變成一次性的了。這是因為count是該實例的變量,所以兩次迭代都是使用的那一個變量,但是該變量第一次循環完之後,就已經超過限制了,所以再次使用for-of循環就得不到任何的結果了。
可以把count變數放在閉包裡,然後透過閉包返回迭代器,這樣子每建立一個迭代器都會對應一個新的計數器。
class Counter {
constructor(limit) {
this.limit = limit }
[Symbol.iterator]() {
let count = 1
const limit = this.limit return {
// 迭代器工廠函數必須要傳回一個有next方法的對象,因為迭代實際上就是透過呼叫next方法來實現的next() {
if (count <= limit) {
return {
done: false,
value: count++
}
} else {
return { done: true, value: undefined }
}
}
}
}}測試
const counter = new Counter(3)for (let i of counter) {
console.log(i)}console.log('另一輪迭代:')for (let i of counter) {
console.log(i)}
就跟使用for-of迴圈一樣,迭代器會很聰明地去呼叫next方法,當迭代器提前終止時,它也會去呼叫return方法。
[Symbol.iterator]() {
let count = 1
const limit = this.limit return {
// 迭代器工廠函數必須要傳回一個有next方法的對象,因為迭代實際上就是透過呼叫next方法來實現的next() {
if (count <= limit) {
return {
done: false,
value: count++
}
} else {
return { done: true, value: undefined }
}
},
return() {
console.log('提前終止迭代器')
return { done: true }
}
}}測試
const counter = new Counter(5)for (let i of counter) {
if (i === 3) {
break;
}
console.log(i)}
如果迭代器沒有關閉,就可以繼續從上次離開的地方繼續迭代。數組地迭代器就是不能關閉的。
const arr = [1, 2, 3, 4, 5]const iter = arr[Symbol.iterator]()iter.return = function () {
console.log('提前退出迭代器')
return {
done: true
}}for (const i of iter) {
console.log(i)
if (i === 2) {
break
}}for (const i of iter) {
console.log(i)}