by @zhangbao(zhangbao) #0104javascript
能夠將可迭代對象理解爲「寬泛意義上的數組」——就是說,不必定是數組(Array.isArray(iterable)
返回 false
),但卻可以被 for...of
循環遍歷。java
[Symbol.iterator]
方法屬性咱們舉一個例子,下面有一個對象:數組
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
咱們先來看下,這個屬性的描述是怎樣的。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
for..of
循環首先會調用對象上的方法屬性 [Symbol.iterator]
——range[Symbol.iterator]()
,獲得一個包含 next
方法的對象。next
方法的對象稱爲迭代對象(iterator object)range[Symbol.iterator]
被稱爲迭代對象生成器或迭代對象生成函數for..of
就是徹底在跟這個迭代對象打交道了,for..of
循環一次,就要調用一次 next
方法,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);
}
複製代碼
range[Symbol.iterator]
方法,得迭代對象while
循環內部:done
爲 true
),就終止循環,結束遍歷前面咱們說過:可迭代對象不必定是數組,如今再加一句:數組必定是可迭代對象。根據經驗,咱們知道數組是能夠用 for...of
循環遍歷的。
遍歷 symbols
被 for...of
循環成功遍歷了。這是意料之中的事情,但咱們再來看下,這個數組對象裏是否是有個叫 Symbol.iterator
的屬性 🕵️♂️
果真!
字符串也是可迭代對象。證據以下:
再來找找 Symbol.iterator
屬性。
Map 和 Set 也是可以被 for...of
遍歷的。在這裏就很少舉例了,直接展現它們各自部署的 Symbol.iterator
屬性。
從上面的截圖裏,咱們能夠總結出一點內容來:
- Map 對象默認的迭代對象生成器函數是
map.entries()
,而- Set 對象默認的迭代對象生成器函數是
map.values()
更多關於 Map 和 Set 對象的遍歷內容,請參考《遍歷 Map 和 Set》。
(完)