最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

js迭代器與可迭代對象終極用法詳解

 更新時間:2026年03月03日 09:34:26   作者:瓏墨  
迭代器是一個對象,它提供了一種標準的方式來遍歷集合中的元素,這篇文章主要介紹了js迭代器與可迭代對象終極用法的相關(guān)資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下

目標:不僅會“用”,還能“設(shè)計、調(diào)試、擴展、優(yōu)化”。文內(nèi)包含從零手寫、生成器、惰性管道、異步流、資源管理、常見坑、性能建議、練習(xí)清單等。

1. 核心協(xié)議

  • 可迭代協(xié)議 (Iterable):對象實現(xiàn) obj[Symbol.iterator](),返回一個迭代器。
  • 迭代器協(xié)議 (Iterator):返回值具備 next() 方法,每次 next() 返回 { value, done }。
  • 消費方for...of、展開 ...、數(shù)組/對象解構(gòu)、Promise.all、new Map(iterable)、new Set(iterable)Array.from 等。
  • 原生可迭代Array、String、Map、Set、TypedArrayarguments、NodeList 等。
const arr = [10, 20];
const it = arr[Symbol.iterator](); // 拿到迭代器
console.log(it.next()); // { value: 10, done: false }
console.log(it.next()); // { value: 20, done: false }
console.log(it.next()); // { value: undefined, done: true }

2. for…of / for…in / for await…of 對比

  • for...of:遍歷“值”,依賴可迭代協(xié)議,順序穩(wěn)定。
  • for...in:遍歷“可枚舉屬性鍵”,含原型鏈可枚舉屬性,不需要可迭代。
  • for await...of:遍歷“異步可迭代”或“值為 Promise 的可迭代”,逐個 await。
const arr = [3, 6, 9];
for (const v of arr) console.log('of =>', v); // 3 6 9
for (const k in arr) console.log('in =>', k); // 0 1 2

3. 從零手寫同步迭代器(含 return/throw)

場景:為自定義對象提供可迭代能力,并處理提前終止。

const counter = {
  current: 1,
  max: 3,
  [Symbol.iterator]() {
    const self = this;
    return {
      next() {
        if (self.current <= self.max) {
          return { value: self.current++, done: false };
        }
        return { value: undefined, done: true };
      },
      return() {
        console.log('迭代被提前終止,執(zhí)行清理邏輯');
        return { value: undefined, done: true };
      },
      throw(err) {
        console.log('外部拋錯被捕獲:', err.message);
        return { value: undefined, done: true };
      },
    };
  },
};

for (const n of counter) {
  console.log(n);
  if (n === 2) break; // 觸發(fā) return()
}

要點:

  • Symbol.iterator 返回的對象必須實現(xiàn) next()
  • done: true 視為終止;value 可省略。
  • return() 可用于 break/return/throw 時的清理;throw() 讓外部異常傳入迭代器。

4. 生成器 (Generator) 深潛:function* / yield / yield*

生成器函數(shù)(function* / async function*)執(zhí)行后返回一個“生成器對象”,它同時是迭代器和可迭代對象。生成器以“暫停/恢復(fù)”的方式運行,內(nèi)部編譯成狀態(tài)機。

4.1 生成器函數(shù) vs 生成器對象

  • 生成器函數(shù):寫法 function* foo() { ... }const foo = function* () { ... };箭頭函數(shù)不能寫成生成器。
  • 生成器對象:調(diào)用生成器函數(shù)得到,如 const it = foo();它擁有 next/return/throw 并實現(xiàn) Symbol.iterator。
function* range(start, end, step = 1) {
  for (let i = start; i <= end; i += step) yield i; // yield 產(chǎn)出,并“暫停”
}
const it = range(1, 3);
console.log(it.next()); // { value: 1, done: false }
console.log(it.next()); // { value: 2, done: false }
console.log(it.next()); // { value: 3, done: false }
console.log(it.next()); // { value: undefined, done: true }

4.2 yield 的雙向通信與狀態(tài)機

next(value) 會把 value 作為“上一個 yield 表達式的結(jié)果”傳回生成器內(nèi)部。

function* dialog() {
  const name = yield '你是誰?';
  const lang = yield `你好,${name},你用什么語言?`;
  return `${name} 使用 ${lang}`;
}
const g = dialog();
console.log(g.next());           // { value: '你是誰?', done: false }
console.log(g.next('Alice'));    // { value: '你好,Alice,你用什么語言?', done: false }
console.log(g.next('JavaScript'));// { value: 'Alice 使用 JavaScript', done: true }

4.3 return / throw:主動收尾與異常注入

  • iter.return(value):立即終止,返回 { value, done: true },觸發(fā)生成器內(nèi)的 finally。
  • iter.throw(err):將錯誤注入生成器,在內(nèi)部可被 try/catch 捕獲;若未捕獲則向外拋出。
function* work() {
  try {
    yield 1;
    yield 2;
  } finally {
    console.log('清理資源');
  }
}
const it2 = work();
console.log(it2.next());      // { value:1, done:false }
console.log(it2.return(99));  // 清理資源 -> { value:99, done:true }

4.4 yield*:委托/扁平化子迭代器,并可接收子迭代器的 return

yield* otherIterable 把“迭代控制權(quán)”交給子迭代器,等價于逐個 for...of 產(chǎn)出其值。yield* 的結(jié)果是子迭代器的 return 值。

function* sub() {
  yield 1;
  yield 2;
  return 9; // 會被 yield* 捕獲
}
function* parent() {
  const ret = yield* sub();   // 產(chǎn)出 1、2,并獲得 ret=9
  yield ret;                  // 再產(chǎn)出 return 值
}
console.log([...parent()]); // [1, 2, 9]

yield* 應(yīng)用:遞歸/扁平化/管道組合

function* flatten(tree) {
  for (const node of tree) {
    if (Array.isArray(node)) yield* flatten(node); // 遞歸委托
    else yield node;
  }
}
console.log([...flatten([1, [2, [3, 4]], 5])]); // [1,2,3,4,5]

4.5 生成器的執(zhí)行特性與調(diào)試要點

  • 惰性:直到調(diào)用 next() 才會繼續(xù)運行;適合大/無限序列。
  • 單次消費:同一個生成器對象不可復(fù)位,需重新創(chuàng)建。
  • 清理:在生成器內(nèi)部用 try/finally;外部可以 return() 觸發(fā)。
  • 不可用箭頭函數(shù):箭頭語法不支持 function*,需常規(guī)函數(shù)寫法。
  • 與 for…offor...of 自動反復(fù) next() 直到 done:true;break/throw/return 會觸發(fā)迭代器的 return()。

5. 可迭代工具箱與常見 API

  • 展開/解構(gòu):[...iterable]const [a, ...rest] = iterable
  • 集合轉(zhuǎn)換:Array.from(iterable)、new Map(iterable)、new Set(iterable)
  • Promise 組合:Promise.all(iterable)、Promise.allSettled、Promise.race(需可迭代)
const set = new Set([1, 2, 3]);
const arr = [...set]; // [1,2,3]
const [first, ...rest] = set; // first=1, rest=[2,3]

6. 自定義數(shù)據(jù)結(jié)構(gòu):可迭代的 Deque(類 + 私有字段)

class Deque {
  #data = [];
  pushFront(x) { this.#data.unshift(x); }
  pushBack(x) { this.#data.push(x); }
  popFront() { return this.#data.shift(); }
  popBack() { return this.#data.pop(); }
  get size() { return this.#data.length; }

  [Symbol.iterator]() {
    let idx = 0;
    return {
      next: () =>
        idx < this.#data.length
          ? { value: this.#data[idx++], done: false }
          : { value: undefined, done: true },
      return() { return { done: true }; },
    };
  }
}

const dq = new Deque();
dq.pushBack(10); dq.pushFront(5); dq.pushBack(20);
for (const v of dq) console.log(v); // 5 10 20

設(shè)計建議:

  • 迭代期間若會修改內(nèi)部存儲,需明確順序定義與終止條件(如記錄快照或用生成器惰性遍歷)。
  • 大數(shù)據(jù)/潛在無限序列優(yōu)先用生成器,避免一次性展開耗內(nèi)存。

7. 惰性管道:map / filter / take / drop

用生成器實現(xiàn)“按需取值”的流式組合。

function* map(iterable, fn) {
  for (const x of iterable) yield fn(x);
}
function* filter(iterable, pred) {
  for (const x of iterable) if (pred(x)) yield x;
}
function* take(iterable, n) {
  if (n <= 0) return;
  let i = 0;
  for (const x of iterable) {
    yield x;
    if (++i >= n) break;
  }
}
function* drop(iterable, n) {
  let i = 0;
  for (const x of iterable) if (i++ >= n) yield x;
}

const src = [1, 2, 3, 4, 5, 6];
const pipeline = take(filter(map(src, x => x * 3), x => x % 2 === 0), 2);
console.log([...pipeline]); // [6, 12]

優(yōu)勢:逐元素計算,適合大數(shù)據(jù)、IO 流;可輕松擴展更多算子(zip、flatMap、chunk、uniq 等)。

8. 異步迭代器與 for await…of

異步可迭代實現(xiàn) Symbol.asyncIteratornext() 返回 Promise,或用 async function*

const asyncCounter = {
  current: 1,
  max: 3,
  async *[Symbol.asyncIterator]() {
    while (this.current <= this.max) {
      await new Promise(r => setTimeout(r, 100));
      yield this.current++;
    }
  },
};

(async () => {
  for await (const n of asyncCounter) console.log(n);
})();

典型場景:分頁 API、網(wǎng)絡(luò)流(ReadableStream)、文件流、數(shù)據(jù)庫游標、消息隊列。

同步可迭代 + Promise 元素

for await...of 也能遍歷“同步可迭代且元素為 Promise”的情況:

const xs = [1, 2, 3].map(v => Promise.resolve(v * 10));
(async () => {
  for await (const v of xs) console.log(v); // 10 20 30
})();

9. 資源管理與提前終止

在生成器中用 try/finally + return() 保障資源釋放。

function* readChunks(reader) {
  try {
    while (true) {
      const chunk = reader.read();
      if (!chunk) break;
      yield chunk;
    }
  } finally {
    reader.close(); // 即便 break/throw 也會執(zhí)行
  }
}

在異步生成器中同理使用 try/finally

async function* streamLines(stream) {
  try {
    for await (const line of stream) yield line;
  } finally {
    stream.destroy?.();
  }
}

10. 常見坑排查表(含錯誤示例)

  • TypeError: object is not iterable:缺少 Symbol.iterator 或拼寫錯誤。
  • 迭代器復(fù)用:多數(shù)迭代器是一次性的,復(fù)用要重新獲取 obj[Symbol.iterator]()。
  • for...ofbreak/throw 卻未清理資源:實現(xiàn) return() 或在生成器用 finally
  • for...of 誤用在異步迭代器:應(yīng)改 for await...of。
  • 隱式耗盡:[...iter] 會一次性拉平,若是大數(shù)據(jù)/無限序列會卡死或 OOM;改用惰性消費。
  • 順序期待:Set/Map 保持插入順序;普通對象屬性遍歷順序有規(guī)則但不屬“可迭代”。

調(diào)試技巧:

const it = someIterable[Symbol.iterator]();
console.log(it.next(), it.next()); // 手動探查序列

11. 性能與工程化建議

  • 惰性優(yōu)先:未知大小或可能無限的來源用生成器/異步生成器。
  • 避免重復(fù)遍歷:對昂貴來源(IO/計算)避免多次消費,可緩存結(jié)果或暴露 toArray()。
  • 批量/背壓:異步流中可結(jié)合 take、chunk、throttle 控制節(jié)奏。
  • 類型提示:在 TS 中為迭代器聲明泛型,避免 any 擴散。
  • 組合優(yōu)先:map/filter/take/drop/flatMap/zip 等算子小而精,利于單測和重用。
  • 清理保證:生成器里用 try/finally;顯式實現(xiàn) return() 以防資源泄漏。

12. 典型模式示例

12.1 管道式數(shù)據(jù)流

function* flatMap(iterable, fn) {
  for (const x of iterable) {
    const res = fn(x);
    if (Symbol.iterator in Object(res)) yield* res;
    else yield res;
  }
}

const words = ['hi', 'js'];
const chars = flatMap(words, w => w.split(''));
console.log([...chars]); // ['h','i','j','s']

12.2 無限序列 + take 限流

function* naturals() { let i = 1; while (true) yield i++; }
console.log([...take(naturals(), 5)]); // [1,2,3,4,5]

12.3 異步分頁封裝

async function* fetchPages(fetchPage) {
  let page = 1;
  while (true) {
    const data = await fetchPage(page);
    if (!data.length) break;
    yield data;
    page += 1;
  }
}

(async () => {
  for await (const page of fetchPages(p => api.list({ page: p }))) {
    console.log('page size', page.length);
  }
})();

12.4 具備回收的文件讀?。∟ode)

const fs = require('fs');
async function* readLines(path) {
  const stream = fs.createReadStream(path, 'utf8');
  try {
    for await (const chunk of stream) yield chunk;
  } finally {
    stream.close();
  }
}

13. FAQ 精要

  • 何時用生成器 vs 普通函數(shù)? 需要“逐步產(chǎn)出/惰性/可中斷/可組合”時用生成器。
  • 迭代器能重置嗎? 原生多數(shù)不可;若需可重復(fù)遍歷,應(yīng)在 Symbol.iterator 中返回“新的迭代器實例”。
  • 如何判斷對象可迭代? obj != null && typeof obj[Symbol.iterator] === 'function'
  • async 迭代器如何并行? 迭代本身是串行消費;并行可在內(nèi)部批量啟動 Promise,再逐個 yield 結(jié)果(注意背壓)。
  • 能否在生成器里用 await? 不能,改用 async function* 或在外層 for await...of

15. 結(jié)語

迭代器與可迭代協(xié)議為 JS 提供統(tǒng)一、可組合的訪問抽象;生成器/異步生成器進一步讓“惰性、流式、可中斷”變得自然。工程落地時,請同時關(guān)注資源釋放、背壓、可測試性與性能可觀測性,把迭代封裝成可靠的基礎(chǔ)設(shè)施。

到此這篇關(guān)于js迭代器與可迭代對象終極用法的文章就介紹到這了,更多相關(guān)js迭代器與可迭代對象內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

方正县| 通辽市| 清水河县| 宝应县| 郧西县| 高青县| 沧州市| 甘肃省| 萍乡市| 喀喇| 通山县| 广宗县| 安仁县| 宁河县| 临海市| 沙坪坝区| 天津市| 商城县| 旺苍县| 潜江市| 蓝山县| 沾化县| 潮安县| 萨嘎县| 大港区| 文山县| 灵武市| 南投县| 柳河县| 调兵山市| 长岭县| 汉阴县| 施甸县| 革吉县| 东平县| 黎川县| 舟山市| 武威市| 县级市| 红河县| 安远县|