首頁 > web前端 > js教程 > 主體

你知道JavaScript中的可迭代物件與迭代器嗎

醉折花枝作酒筹
發布: 2021-08-09 18:02:03
轉載
2618 人瀏覽過

迭代是訪問集合元素的一種方法;可以被迭代的對象稱為可迭代對象;迭代器是一個可以記住遍歷位置的對象,迭代器對象從集合的第一個元素開始訪問,直到所有元素被存取結束,迭代器只能前進不會後退。

Lazy evaluation

Lazy evaluation常被翻譯為“延遲計算”或“惰性計算”,指的是僅在真正需要執行的時候才計算表達式的值。

與惰性求值相反的是及早求值(eager evaluation)及早求值,也稱為貪婪求值(greedy evaluation)或嚴格求值,是多數傳統程式語言的求值策略。

充分利用惰性求值的特性所帶來的好處主要體現在以下兩個面向:

  • 避免不必要的運算,帶來效能上的提升。

  • 節省空間,使得無限迴圈的資料結構成為可能。

迭代器

ES6 中的迭代器使惰性求值和建立使用者定義的資料序列成為可能。迭代是一種遍歷資料的機制。迭代器是用於遍歷資料結構元素(稱為Iterable)的指針,用於產生值序列的指針。

迭代器是一個可以被迭代的物件。它抽象化了資料容器,使其行為類似於可迭代物件。

迭代器在實例化時不會計算每個項目的值,僅在請求時才產生下一個值。這非常有用,特別是對於大型資料集或無限個元素的序列。

可迭代物件

可迭代物件是希望其元素可被公眾存取的資料結構。 JS 中的許多物件都是可迭代的,它們可能不是很好的察覺,但是如果仔細檢查,就會發現迭代的特徵:

  • new Map([iterable])

  • new WeakMap([iterable])

  • #new Set([iterable])

  • new WeakSet([iterable])

  • Promise.all([iterable])

  • Promise.race([iterable])

  • Array.from([iterable])

還有需要一個可迭代的對象,否則,它將拋出一個類型錯誤,例如:

  • for ... of

  • ... (展開運算子)
    const [a, b, ..] = iterable (解構賦值)

  • yield* (生成器)

JavaScript中已有許多內建的可迭代項目:

String,Array,TypedArray,Map,Set。

迭代協定

迭代器和可迭物件遵循迭代協定。

協定是一組接口,並規定如何使用它們。

迭代器遵循迭代器協議,可迭代遵循可迭代協議。

可迭代的協定

要讓物件變得可迭代,它必須實作一個透過Symbol.iterator的迭代器方法,這個方法是迭代器的工廠。

使用 TypeScript,可迭代協定如下所示:

interface Iterable {
  [Symbol.iterator]() : Iterator;
}
登入後複製

Symbol.iterator]()是無參數函數。在可迭代對像上呼叫它,這意味著我們可以透過this來存取可迭代對象,它可以是常規函數或生成器函數。

迭代器協定

迭代器協定定義了產生值序列的標準方法。

為了讓物件成為迭代器,它必須實作next()方法。迭代器可以實作return()方法,我們將在本文後面討論這個問題。

使用TypeScript,迭代器協定如下所示:

interface Iterator {
    next() : IteratorResult;
    return?(value?: any): IteratorResult;
}
登入後複製

IteratorResult 的定義如下:

interface IteratorResult {
    value?: any;
    done: boolean;
}
登入後複製
  • done通知消費者迭代器是否已被使用,false表示仍有值需要生成,true表示迭代器已經結束。

  • value 可以是任何 JS 值,它是向消費者展示的值。

當done為true時,可以省略value。

組合

迭代器和可以迭代物件可以用下面這張圖來表示:

你知道JavaScript中的可迭代物件與迭代器嗎

事例

基礎知識介紹完了,接著,我們來配合一些例子來加深我們的映像。

範圍迭代器

我們先從一個非常基本的迭代器開始,createRangeIterator迭代器。

我們手動呼叫it.next()以獲得下一個IteratorResult。最後一次呼叫傳回{done:true},這表示迭代器現在已被使用,不再產生任何值。

function createRangeIterator(from, to) {
  let i = from;

  return {
    next() {
      if (i <= to) {
        return { value: i++, done: false };
      } else {
        return { done: true };
      }
    }
  }
}

const it = createRangeIterator(1, 3);

console.log(it.next());
console.log(it.next());
console.log(it.next());
console.log(it.next());
登入後複製

你知道JavaScript中的可迭代物件與迭代器嗎

可迭代範圍迭代器

在本文的前面,我已經提到JS 中的某些語句需要一個可迭代的物件。因此,我們前面的範例在與for ... of循環一起使用時將不起作用。

但是建立符合迭代器和可迭代協定的物件非常容易。

你知道JavaScript中的可迭代物件與迭代器嗎

function createRangeIterator (from, to) {
  let i = from

  return {
    [Symbol.iterator] () {
      return this
    },
    next() {
      if (i <= to) {
        return { value: i++, done: false }
      } else {
        return { done: true }
      }
    }
  }
}

const it = createRangeIterator(1, 3)

for (const i of it) {
  console.log(i)
}
登入後複製

无限序列迭代器

迭代器可以表示无限制大小的序列,因为它们仅在需要时才计算值。

注意不要在无限迭代器上使用扩展运算符(...),JS 将尝试消费迭代器,由于迭代器是无限的,因此它将永远不会结束。 所以你的应用程序将崩溃,因为内存已被耗尽

同样,for ... of 循环也是一样的情况,所以要确保能退出循环:

function createEvenNumbersIterator () {
  let value = 0

  return {
    [Symbol.iterator] () {
      return this
    },
    next () {
      value += 2
      return { value, done: false}
    }
  }
}

const it = createEvenNumbersIterator()

const [a, b, c] = it
console.log({a, b, c})

const [x, y, z] = it
console.log({ x, y, z })

for (const even of it) {
  console.log(even)
  if (even > 20) {
    break
  }
}
登入後複製

你知道JavaScript中的可迭代物件與迭代器嗎

关闭迭代器

前面我们提到过,迭代器可以有选择地使用return()方法。 当迭代器直到最后都没有迭代时使用此方法,并让迭代器进行清理。

for ... of循环可以通过以下方式更早地终止迭代:

  • break

  • continue

  • throw

  • return

function createCloseableIterator () {
  let idx = 0
  const data = [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;, &#39;e&#39;]

  function cleanup() {
    console.log(&#39;Performing cleanup&#39;)
  }
  return {
    [Symbol.iterator]() { return this },
    next () {
      if (idx <= data.length - 1) {
        return { value: data[idx++], done: false }
      } else {
        cleanup()
        return { done: true }
      }
    },
    return () {
      cleanup()
      return { done: true }
    }
  }
}

const it = createCloseableIterator()

for (const value of it) {
  console.log(value)
  if (value === &#39;c&#39;) {
    break
  }
}

console.log(&#39;\n----------\n&#39;)

const _it = createCloseableIterator();
for (const value of _it) {
  console.log(value);
}
登入後複製

你知道JavaScript中的可迭代物件與迭代器嗎

  • 如果知道迭代器已经结束,则手动调用cleanup()函数。

  • 如果突然完成,则return()起作用并为我们进行清理。

【推荐学习:javascript高级教程

以上是你知道JavaScript中的可迭代物件與迭代器嗎的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:segmentfault.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!