說起迭代器, 那就要先瞭解迭代模式 迭代模式: 提供一種方法可以順序獲得聚合對象中的各個元素, 是一種最簡單, 也是最常見的設計模式,它可以讓用戶通過特定的介面尋訪集合中的每一個元素 而不用瞭解底層的實現。 迭代器 : 依照迭代模式的思想而實現, 分為內部迭代器和外部迭代器, 內部迭代器: 本身是函 ...
說起迭代器, 那就要先瞭解迭代模式
迭代模式: 提供一種方法可以順序獲得聚合對象中的各個元素, 是一種最簡單,
也是最常見的設計模式,它可以讓用戶通過特定的介面尋訪集合中的每一個元素
而不用瞭解底層的實現。
迭代器 : 依照迭代模式的思想而實現,
分為內部迭代器和外部迭代器,
內部迭代器: 本身是函數, 該函數內部定義好迭代規則,完全接手整個迭代過程
外部只需要一次初始調用,如Array.prototype.forEach, Jquery.each
外部迭代器: 本身是函數, 執行返回迭代對象,迭代下一個元素必須顯示調用,
調用複雜度增加,但靈活性增強。
我們主要來瞭解一下外部迭代器:
外部迭代器本身是一個函數, 執行這個函數就相當於啟動了這個迭代器,
然後我們每次迭代需要手動調用next()方法,返回一個對象,
基於此我們來自己實現一個外部迭代器
const arr = [2,3,4]; function OuterIterator(o) { let curIndex = 0; let next = () => { return { value: o[curIndex], done: o.length === ++curIndex, } }; return { next } } const oIt = OuterIterator(arr); console.log(oIt.next()); console.log(oIt.next()); console.log(oIt.next());
結果:
我們為什麼要用外部迭代器呢 ?
想象一個場景, 後端給前端返回一個數組形式的數據, 前端通過for 迴圈遍歷
當業務變動時, 後端傳給前端的不再是數組了, 而是一個對象又
或者是一個Map/Set 結構的數據, 那前端的遍歷代碼就需要大規模重寫。
所以我們要標準化迭代操作,
解決方案: ES6 引入Iterator, 部署在NodeLlist, argument, Array, Set, Map , 字元串
等數據上的Symbol.iterator屬性 , 使得這些數據是可迭代的, 並可進行
for...of , ... , Array.from等操作。
這裡插播一個內容Symbol: js 第七種 基本數據結構
特點: 唯一, 可作為對象屬性, 有靜態方法Symbol.iterator
Symbol如果傳入的是對象的話,會隱式調用對象的toString() 方法,比如:
let os = Symbol({'name': 'liu'}); console.log(os); let os2 = Symbol({'name': 'liu', toString() { return "我最帥" }}); console.log(os2);
我們重寫了對象的toString方法, 結果:
回到主題, 原生有iterator介面的只有NodeLlist, argument, Array, Set, Map , 字元串等數據
對象上是沒有iterator的, 所以它不能被迭代, 也不能進行for...of , ... , Array.from等操作
那我們要迭代對象怎麼辦呢 ?
沒錯, 就是給它加上我們自己按照ES6規範寫外部迭代器:
const obj = {
0: 'liu',
1: '18',
2: 'man',
}; console.log([...obj]);
直接用...操作符報錯, obj 不可迭代
const obj = { 0: 'liu', 1: '18', 2: 'man', length: 4, [Symbol.iterator] : function () { let curIndex = 0; let next = () => { return { value: this[curIndex], done: this.length === ++curIndex, } }; return { next } } }; console.log([...obj]);
結果:
obj必須是類數組, 就是屬性是數字 , 且有length屬性 才能這樣寫。
我們下一篇來討論generator生成器, 它可以生成一個迭代對象。