一文詳解JavaScrip可迭代對(duì)象(Iterable)
為什么需要“可迭代”?
在 ES6 之前,我們遍歷數(shù)據(jù)主要靠 for 循環(huán)(基于索引)或 forEach。但這種方式有兩個(gè)痛點(diǎn):
- 不通用:數(shù)組能遍歷,但字符串、DOM 節(jié)點(diǎn)列表、Map、Set 的遍歷方式各不相同。
- 不靈活:無法自定義遍歷邏輯(比如我想倒序遍歷,或者只遍歷偶數(shù)項(xiàng))。
為了解決這個(gè)問題,ES6 引入了 迭代器 (Iterator) 和 可迭代對(duì)象 (Iterable) 的概念,統(tǒng)一了數(shù)據(jù)訪問的接口。
通俗比喻:
想象你去一家自助餐廳吃飯。
- 普通對(duì)象:像是一堆散落在桌子上的菜,沒有順序,你只能指著說“我要那個(gè)”,不能按順序一個(gè)個(gè)拿。
- 可迭代對(duì)象:像是傳送帶上的壽司。傳送帶有一個(gè)機(jī)制(迭代器),讓你可以一個(gè)一個(gè)地按順序獲取食物,直到吃完為止。
Symbol.iterator 就是那個(gè)“啟動(dòng)傳送帶的按鈕”。只要一個(gè)對(duì)象身上有這個(gè)按鈕,它就是“可迭代的”。
1. 核心概念:誰是誰?
很多初學(xué)者容易混淆這兩個(gè)術(shù)語,我們先厘清定義:
可迭代對(duì)象 (Iterable)
- 定義:實(shí)現(xiàn)了 可迭代協(xié)議 的對(duì)象。
- 特征:對(duì)象內(nèi)部必須有一個(gè)鍵為
Symbol.iterator的方法,該方法返回一個(gè)迭代器對(duì)象。 - 常見例子:
Array,String,Map,Set,arguments,NodeList。
迭代器 (Iterator)
- 定義:實(shí)現(xiàn)了 迭代器協(xié)議 的對(duì)象。
- 特征:擁有一個(gè)
next()方法,每次調(diào)用返回一個(gè)包含value和done的對(duì)象。 - 作用:它是真正的“工人”,負(fù)責(zé)逐個(gè)吐出數(shù)據(jù)。
簡單關(guān)系:
可迭代對(duì)象 是工廠,迭代器 是工廠派出的快遞員。
當(dāng)你使用 for...of 時(shí),JS 引擎會(huì)自動(dòng)調(diào)用工廠的 Symbol.iterator 拿到快遞員,然后一直喊 next() 直到送完貨。
2. 底層原理:兩個(gè)協(xié)議
協(xié)議一:可迭代協(xié)議 (Iterable Protocol)
只要對(duì)象擁有 [@@iterator] 方法(即 Symbol.iterator 屬性),它就是可迭代的。
const arr = [1, 2, 3]; // 檢查是否有 Symbol.iterator 方法 console.log(typeof arr[Symbol.iterator]); // "function"
協(xié)議二:迭代器協(xié)議 (Iterator Protocol)
Symbol.iterator 方法返回的對(duì)象,必須滿足以下條件:
- 擁有
next()方法。 next()返回一個(gè)對(duì)象{ value: any, done: boolean }。value: 當(dāng)前產(chǎn)出的值。done: 是否遍歷結(jié)束(true表示結(jié)束)。
3. 代碼實(shí)戰(zhàn):判斷與使用
場(chǎng)景一:哪些是原生的可迭代對(duì)象?
// ? 數(shù)組
[1, 2, 3][Symbol.iterator]; // function
// ? 字符串
"hello"[Symbol.iterator]; // function
// ? Map
new Map()[Symbol.iterator](
// function
// ? 普通對(duì)象
{},
)[Symbol.iterator]; // undefined -> 不可迭代!
場(chǎng)景二:for...of的本質(zhì)
當(dāng)你寫這段代碼時(shí):
for (const item of [1, 2, 3]) {
console.log(item);
}
JavaScript 引擎實(shí)際上在執(zhí)行以下操作:
const arr = [1, 2, 3];
const iterator = arr[Symbol.iterator](); // 1. 獲取迭代器
let result = iterator.next(); // 2. 第一次 next()
while (!result.done) {
// 3. 如果沒做完
console.log(result.value); // 4. 處理值
result = iterator.next(); // 5. 繼續(xù)下一個(gè)
}
4. 手寫實(shí)現(xiàn):讓普通對(duì)象可迭代
這是面試的高頻考點(diǎn):如何讓一個(gè)普通對(duì)象支持 for...of?
假設(shè)我們有一個(gè)范圍對(duì)象 Range,我們希望它能遍歷出從 start 到 end 的數(shù)字。
const range = {
start: 1,
end: 5,
};
// ? 直接遍歷會(huì)報(bào)錯(cuò):range is not iterable
// for (let num of range) { ... }
// ? 改造:添加 Symbol.iterator 方法
range[Symbol.iterator] = function () {
let current = this.start;
const last = this.end;
// 返回一個(gè)符合“迭代器協(xié)議”的對(duì)象
return {
next() {
if (current <= last) {
return { value: current++, done: false };
} else {
return { value: undefined, done: true };
}
},
};
};
// 現(xiàn)在可以愉快地遍歷了!
for (let num of range) {
console.log(num); // 1, 2, 3, 4, 5
}
// 也可以使用展開運(yùn)算符
console.log([...range]); // [1, 2, 3, 4, 5]
關(guān)鍵點(diǎn):Symbol.iterator 函數(shù)必須返回一個(gè)新的迭代器對(duì)象。這樣即使嵌套遍歷或多次遍歷,狀態(tài)也不會(huì)沖突。
5. 常見誤區(qū):Array-like vs Iterable
很多“類數(shù)組對(duì)象”(Array-like)不是可迭代對(duì)象,這經(jīng)常導(dǎo)致 Bug。
誤區(qū):arguments和NodeList
在舊版瀏覽器或某些環(huán)境下,document.querySelectorAll 返回的 NodeList 可能沒有 Symbol.iterator(雖然現(xiàn)代瀏覽器大多已支持,但需警惕兼容性)。
const divs = document.querySelectorAll("div");
// ? 在某些舊環(huán)境或特定對(duì)象上可能失效
// for (let div of divs) { ... }
// ? 安全做法:使用 Array.from 轉(zhuǎn)換
const divArray = Array.from(divs);
divArray.forEach((div) => console.log(div));
最佳實(shí)踐:如何安全地遍歷?
如果你不確定一個(gè)對(duì)象是否可迭代,可以使用以下工具函數(shù)檢測(cè):
function isIterable(obj) {
// 檢查 null/undefined
if (obj == null) return false;
// 檢查是否有 Symbol.iterator 方法且類型為函數(shù)
return typeof obj[Symbol.iterator] === "function";
}
console.log(isIterable([])); // true
console.log(isIterable({})); // false
console.log(isIterable("")); // true
6. 總結(jié)
| 概念 | 說明 | 關(guān)鍵方法/屬性 |
|---|---|---|
| 可迭代對(duì)象 | 能被 for...of 遍歷的對(duì)象 | obj[Symbol.iterator] |
| 迭代器 | 負(fù)責(zé)逐個(gè)返回?cái)?shù)據(jù)的對(duì)象 | iterator.next() |
| next() 返回值 | 包含值和狀態(tài)的對(duì)象 | { value: ..., done: ... } |
| 原生可迭代 | Array, String, Map, Set, arguments 等 | - |
| 非可迭代 | 普通 Object {} | - |
博主寄語:
可迭代協(xié)議是 JavaScript 統(tǒng)一數(shù)據(jù)訪問方式的偉大嘗試。
它不僅讓 for...of 成為可能,還支撐起了生成器 (Generator)、異步迭代器 (Async Iterator) 等高級(jí)特性。
記住口訣:
想要遍歷用 for...of,
對(duì)象必須有 iterator。next 方法吐數(shù)據(jù),done 為真停腳步。
以上就是一文詳解JavaScrip可迭代對(duì)象(Iterable)的詳細(xì)內(nèi)容,更多關(guān)于JavaScrip可迭代對(duì)象Iterable的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
javascript中import和export用法示例總結(jié)
在 ES6 之前,社區(qū)制定了一些模塊加載方案,最主要的有 CommonJS 和 AMD 兩種,前者用于服務(wù)器,后者用于瀏覽器,這篇文章給大家介紹了javascript中import和export用法總結(jié),感興趣的朋友一起看看吧2025-05-05
點(diǎn)擊彈出層效果&彈出窗口后網(wǎng)頁背景變暗效果的實(shí)現(xiàn)代碼
本篇文章主要是對(duì)點(diǎn)擊彈出層效果&彈出窗口后網(wǎng)頁背景變暗效果的實(shí)現(xiàn)代碼進(jìn)行了介紹,需要的朋友可以過來參考下,希望對(duì)大家有所幫助2014-02-02
Web純前端“旭日?qǐng)D”實(shí)現(xiàn)元素周期表
本文主要介紹了Web純前端“旭日?qǐng)D”實(shí)現(xiàn)元素周期表的實(shí)例解析。具有很好的參考價(jià)值。下面跟著小編一起來看下吧2017-03-03
JavaScript實(shí)現(xiàn)Tab選項(xiàng)卡切換
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)Tab選項(xiàng)卡切換效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-02-02
jquery+css3實(shí)現(xiàn)網(wǎng)頁背景花瓣隨機(jī)飄落特效
在qq空間可以自定義一些插件,裝飾空間,大家通常就是復(fù)制代碼到空間粘貼,會(huì)實(shí)現(xiàn)非常美觀的效果,有的會(huì)隨機(jī)飄落一些花瓣,那么這種效果用代碼怎么實(shí)現(xiàn)的呢,下面小編給大家詳解jquery實(shí)現(xiàn)網(wǎng)頁背景花瓣隨機(jī)飄落特效,需要的朋友可以參考下2015-08-08
微信小程序?qū)W習(xí)筆記之目錄結(jié)構(gòu)、基本配置圖文詳解
這篇文章主要介紹了微信小程序?qū)W習(xí)筆記之目錄結(jié)構(gòu)、基本配置,結(jié)合實(shí)例形式詳細(xì)分析了微信小程序的相關(guān)注冊(cè)、配置及基本使用方法,并配以圖片加以說明,需要的朋友可以參考下2019-03-03

