什麼是可迭代對象(Iterable objects)?

by @zhangbao(zhangbao) #0104javascript

能夠將可迭代對象理解爲「寬泛意義上的數組」——就是說,不必定是數組(Array.isArray(iterable) 返回 false),但卻可以被 for...of 循環遍歷。java

概覽

  • 可迭代對象不必定是數組,數組必定是可迭代對象。
  • 每一個可迭代對象必然包含一個 [Symbol.iterator]  方法屬性
  • 字符串也是可迭代對象

image.png

改造普通對象

咱們舉一個例子,下面有一個對象:數組

let range = {
  from: 1,
  to: 5
};

// 咱們想用 for..of 遍歷 range,獲得從 1(from) 到 5(to) 的天然數
// for(let num of range) { consol.log(num) } // 遍歷結果 1 -> 2 -> 3 -> 4 -> 5
複製代碼

明眼人一看,就知道 range 不就是個普通對象嘛,跟可迭代對象有什麼關係呢?還要用 for..of 遍歷,遍歷結果還要是 1 -> 2 -> 3 -> 4 -> 5,這不扯淡呢嗎?說的對,如今確定是扯淡,那是由於咱們啥都沒作呢,可不是扯淡嗎?函數

爲了能讓 range 這個普通對象變爲可迭代對象,咱們須要先來了解下 Symbol.iterator 這個系統預置變量。ui

Symbol.iterator

咱們先來看下,這個屬性的描述是怎樣的。this

Object.getOwnPropertyDescriptor(Symbol, 'iterator')
// {
// value: Symbol(Symbol.iterator),
// writable: false,
// enumerable: false,
// configurable: false
// }
複製代碼

由結果可知,這個屬性咱們是修改不了的。spa

下面咱們爲上面的 range 對象添加一個 [Symbol.iterator]  方法屬性——對的,這個屬性的屬性值是函數,每一個可迭代對象都有一個 [Symbol.iterator]  方法屬性,沒有的話,確定不是可迭代對象code

let range = {
  from: 1,
  to: 5
}

// 1. for..of 循環首先會調用對象上的 [Symbol.iterator] 屬性——range[Symbol.iterator](),
// 屬性 range[Symbol.iterator] 稱爲「迭代對象生成器」或「迭代對象生成函數」
range[Symbol.iterator] = function() {
	
  // range[Symbol.iterator]() 的調用結果,會返回一個包含 next 方法的對象,
  // 這個對象稱爲「迭代對象」
  // 2. 接下來, for..of 就是徹底在跟這個迭代對象打交道了
  return {
    current: this.from,
    last: this.to,
    
    // 3. 每次 for..of 循環一次,就要調用一次 next 方法
    next() {
      // 4. 從 next 方法返回的對象中,咱們能得到當前遍歷的值(value)以及遍歷是否結束的標記(done)
      if (this.current <= this.last) {
        return { done: false, value: this.current++ }
      } else {
        return { done: true }
      }
    }
  }
}
複製代碼

for...of 循環遍歷的本質是:cdn

  1. for..of 循環首先會調用對象上的方法屬性 [Symbol.iterator]——range[Symbol.iterator](),獲得一個包含 next 方法的對象。
  • 這個包含 next 方法的對象稱爲迭代對象(iterator object)
  • 屬性 range[Symbol.iterator] 被稱爲迭代對象生成器迭代對象生成函數
  1. 接下來, for..of 就是徹底在跟這個迭代對象打交道了,
  2. 每次 for..of 循環一次,就要調用一次 next 方法,
  3. next 方法返回的對象中({ done: ..., value: ... }),咱們能得到當前遍歷的值(value)以及遍歷是否結束的標記(done)。

通過上面的敘述,咱們還能夠將可迭代對象定義爲:可以生成「迭代對象」的對象。
** 對象

遍歷改造對象

如今對改造後的 range 對象進行遍歷。

let range = {
  from: 1,
  to: 5
}
range[Symbol.iterator] = function() {...}

for (let num of range) {
  console.log(num) // 1, 而後是 2, 3, 4, 5
}
複製代碼

很酷啊,如今能夠一次遍歷出 1 -> 2 -> 3 -> 4 -> 5 這 5 個數字了。

手動遍歷可迭代對象

由於好玩,我們模仿 for...of 循環內部執行流程,純手工寫一下遍歷可迭代對象的邏輯吧。

這裏會用到 while 循環:

// 下面的寫法,等同於
// for (let num of range) { console.log(num) };

let iterator = range[Symbol.iterator]();

while (true) {
  let result = iterator.next();
  // 標記結束(done 爲 true),就終止循環,結束遍歷
  if (result.done) break;
  // 不然,打印當前遍歷的值
  console.log(result.value);
}
複製代碼
  1. 首先,手動調用 range[Symbol.iterator] 方法,得迭代對象
  2. while 循環內部:
  3. 若是標記結束(donetrue),就終止循環,結束遍歷
  4. 不然,打印當前遍歷的值

內置可迭代對象

前面咱們說過:可迭代對象不必定是數組,如今再加一句:數組必定是可迭代對象。根據經驗,咱們知道數組是能夠用 for...of 循環遍歷的。

數組

image.png

遍歷 symbolsfor...of 循環成功遍歷了。這是意料之中的事情,但咱們再來看下,這個數組對象裏是否是有個叫 Symbol.iterator 的屬性 🕵️‍♂️

image.png

果真!

字符串

字符串也是可迭代對象。證據以下:

image.png

再來找找 Symbol.iterator 屬性。

image.png

Map 和 Set

Map 和 Set 也是可以被 for...of 遍歷的。在這裏就很少舉例了,直接展現它們各自部署的  Symbol.iterator 屬性。

image.png
image.png

從上面的截圖裏,咱們能夠總結出一點內容來:

  1. Map 對象默認的迭代對象生成器函數是 map.entries(),而
  2. Set 對象默認的迭代對象生成器函數是 map.values()

更多關於 Map 和 Set 對象的遍歷內容,請參考《遍歷 Map 和 Set》

(完)

相關文章
相關標籤/搜索