迭代器是一種設計模式,可在容器對象如:鏈表、數組上遍歷,無需關心容器對象的內存分配的實現細節。簡單的理解就是可以一個一個的依次拿到其中的數據,類似一個移動的指針,但是會告訴我們什么時候結束。這樣我們拿到數據之后可以做一些需要做的事情。
一、什么是迭代器
在JAVAScript中迭代器是一個特殊對象,這個迭代器對象有一個next()方法,每次調用都返回一個對象(結果對象)。結果對象有兩個屬性:一個是value,表示下一個將要返回的值;另一個是done,它是一個布爾類型的值,如果已經迭代到序列中的最后一個值,則它為 true。迭代器還會保存一個內部指針,用來指向當前集合中值的位置,每調用一次next()方法,都會返回下一個可用的值,類似下面這個對象的結構。
{
next: function () {
return {
value:'',
done: true / false
}
}
}
隨著JavaScript語言的能力進一步提升,新增了一些新的數據類型 如 Map、Set、WeakMap 等,為了這些不同的數據結構,可以統一的迭代,es6 增加了迭代協議這個東西。
迭代協議并不是新的內置實現或語法,而是協議。這些協議可以被任何遵循某些約定的對象來實現。
迭代協議具體分為兩個協議:可迭代協議和迭代器協議。
可迭代協議
要成為可迭代對象, 一個對象必須實現 @@iterator 方法。這意味著對象(或者它原型鏈上的某個對象)必須有一個鍵為 @@iterator 的屬性,可通過常量 Symbol.iterator 訪問該屬性:
簡單的理解,你想讓一個東西可以遍歷,那么這個東西要有一個 @@iterator ,這個屬性可以通過Symbol.iterator 訪問
很多內置類型都實現了 Iterator 接口。包括字符串、數組、Map、Set、arguments 對象。
// 沒有實現迭代器工廠函數的類型
const num = 1;
const obj = {};
console.log(num[Symbol.iterator]); // undefined
console.log(obj[Symbol.iterator]); // undefined
// 實現了迭代器工廠函數的類型
const arr = [1, 2, 3];
const set = new Set([1, 2, 3]);
console.log(arr[Symbol.iterator]); // [Function: values]
console.log(set[Symbol.iterator]); // [Function: values]
實際寫代碼過程中,不需要顯式調用這個工廠函數來生成迭代器。實現可迭代協議的所有類型都會自動兼容接收可迭代對象的任何語言特性。接收可迭代對象的原生語言特性包括:for-of 循環、數組解構、擴展操作符、Array.from()、創建集合、創建映射等等。
這些原生的語言結構會在后臺調用提供的可迭代對象的這個工廠函數,從而創建一個迭代器。
const arr = [1, 2, 3];
// for-of 循環
for (const item of arr) {
console.log(item); // 1 2 3
}
// 數組解構
const [a, b, c] = arr;
console.log(a, b, c); // 1 2 3
// 擴展操作符
let arr2 = [...arr];
console.log(arr2); // [1, 2, 3]
// 使用 Array.from() 賦值數組
let arr3 = Array.from(arr);
console.log(arr3); // [1, 2, 3]
// 創建集合
let set = new Set(arr);
console.log(set); // Set(3) {1, 2, 3}
// 創建映射
let map = new Map(arr.map((x, i) => [i, x]));
console.log(map);
// Map(3) { 0 => 1, 1 => 2, 2 => 3 }
迭代器協議
迭代器協議定義了產生一系列值(無論是有限個還是無限個)的標準方式。當值為有限個時,所有的值都被迭代完畢后,則會返回一個默認返回值。
只有實現了一個擁有語義的 next() 方法,一個對象才符合迭代器協議。
二、迭代器的使用
通過可迭代對象中的迭代器工廠函數 Symbol.iterator來生成迭代器
const arr = []
console.log(arr)
迭代器的next方法
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());
可以發現,迭代器是取完最后一個值之后,即迭代器下一個值 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());
完成并不完成
當我們迭代到 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());
三、自定義迭代器
const obj = {
name: "張三",
age: 18,
};
for (const iterator of obj) {
console.log(obj);
}
對象是沒有實現迭代器,所以不能遍歷對象,為了可以實現對象的遍歷,我們需要在對象上實現上面說的迭代器。
const obj = {
name: "張三",
age: 18,
[Symbol.iterator]: () => {
let index = -1,
atrrList = Object.keys(obj);
const objIterator = {
next: () => {
let result = "";
index++;
if (index < atrrList.length) {
result = {
value: atrrList[index],
done: false,
};
} else {
result = {
done: true,
};
}
return result;
},
};
return objIterator;
},
};
for (const iterator of obj) {
console.log("屬性名:" + iterator + ", 屬性值:" + obj[iterator]);
}
總結
迭代協議可以總結為,一個東西要遍歷,必須滿足可迭代協議跟迭代器協議
可迭代協議是要求對象必須有@@iterator,可以通過Symbol.iterator 訪問,而迭代器協議是一個對象,這個對象的next() 函數返回一個對象,這個對象包括兩個屬性,一個是value,一個是done(boolean,是否是最后一個元素,done 為 true 時 value 可省略)
也就是說迭代器對象本質上,就是一個指針對象,通過指針對象的next(),用來移動指針。