本文章轉載於此處
是什么使得一個編程語言的新特性很棒?當這個特性可以結合多個其它特性的時候。
ES2015
版本中引入的 for...of
語句就是這種情況。
for...of
可以迭代數組、類數組以及任何可以迭代的對象(maps
、sets
、DOM集合
),並且,for...of
的語句還很短。
在這篇文章中,我將會演示 for...of
的能力。
1. 數組迭代
for...of
最常見的應用是對數組項進行迭代。該循環可以高效得完成迭代,而無需其他變量來保持索引。
例如:
const products = ['oranges', 'apples'];
for (const product of products) {
console.log(product);
}
// 'oranges'
// 'apples'
for...of
循環遍歷 products
的每一項。迭代項被賦值給變量 product
.
數組方法 entries()
可以用於訪問迭代項的索引。該方法在每次迭代時返回一對 [index,item]
。
就地解構是 for...of
的另一個重要功能,我們將在下一部分中對其進行詳細說明。
1.1 就地解構
首先,我們來看一下 for...of
循環的語法:
for (LeftHandSideExpression of Expression) {
// statements
}
LeftHandSideExpression
表達式可以替換為任意賦值表達式左側的內容。
在前面的示例中,LeftHandSideExpression
是一個變量聲明 const product
,甚至是一個解構 const [index,product]
。
因此,for...of
的語法支持實現迭代項的解構。
讓我們遍歷一個對象數組,提取每個對象的 name
屬性:
const persons = [
{ name: 'John Smith' },
{ name: 'Jane Doe' }
];
for (const { name } of persons) {
console.log(name);
}
// 'John Smith'
// 'Jane Doe'
const { name } of persons
循環迭代 persons
對象數組,並且就地將 person
對象進行了解構。
2. 類數組迭代
for...of
可以用於迭代類數組對象。arguments
是函數體內的特殊變量,包含函數的所有參數,這是一個經典的類數組對象。
讓我們寫一個求和函數 sum(num1, num2, ..., numN)
:
function sum() {
let sum = 0;
for (const number of arguments) {
sum += number;
}
return sum;
}
sum(1, 2, 3); // => 6
在每次迭代中,for...of
循環遍歷類數組 arguments
中的每一個數,並計算總和。
3. 快速了解可迭代
什么是可迭代對象?它是支持可迭代協議的對象。
我們可以通過查看 Symbol.iterator
方法來確定某個數據是否可迭代。例如,下面的例子顯示了數組是可迭代的:
const array = [1, 2, 3];
const iterator1 = array[Symbol.iterator]();
iterator1.next(); // => { value: 1, done: false }
如果你想了解更多信息,可以隨時閱讀我之前的文章。
for...of
接受可迭代對象。這很棒,因為現在你可以遍歷string
、數組、類數組、set
、map
,同時仍可以享受 for...of
的簡潔。
4. 字符串迭代
JavaScript
的基礎類型 string
是可迭代的。因此,可以輕松地遍歷字符串的字符。
const message = 'hello';
for (const character of message) {
console.log(character);
}
// 'h'
// 'e'
// 'l'
// 'l'
// 'o'
message
是一個字符串。由於字符串可迭代的,因此 for...of
循環遍歷 message
。
5. Map 和 Set 迭代
Map
是一個特殊的對象,將鍵與值相關聯。鍵可以是任何基本類型(通常是 string
,但可以是 number
等)。
幸運的是,Map
也是可迭代的(在鍵/值對上進行迭代),並且 for...of
可以輕松地循環迭代所有鍵/值對。
一起看一下:
const names = new Map();
names.set(1, 'one');
names.set(2, 'two');
for (const [number, name] of names) {
console.log(number, name);
}
// logs 1, 'one'
// logs 2, 'two'
for (const [number, name] of names)
迭代 names
的鍵值對。
在每個循環中,迭代器都會返回一個數組 [key,value]
,並使用 const [number,name]
立即對這對數組進行解構。
以相同的方式可以遍歷 Set
的項:
const colors = new Set(['white', 'blue', 'red', 'white']);
for (color of colors) {
console.log(color);
}
// 'white'
// 'blue'
// 'red'
6. 迭代普通的JavaScript對象
嘗試遍歷普通JS對象的屬性/值總是很痛苦。過去,我通常使用 Object.keys()
獲取對象的鍵,然后使用 forEach
來迭代鍵數組。【譯者:這不代表本人觀點,我比較喜歡用 for...in
遍歷對象,不過要注意 for...in 的副作用】
const person = {
name: 'John Smith',
job: 'agent'
};
Object.keys(person).forEach(prop => {
console.log(prop, person[prop]);
});
// 'name', 'John Smith'
// 'job', 'agent'
新的 Object.entries()
函數與 for...of
組合使用是個不錯的選擇:
const person = {
name: 'John Smith',
job: 'agent'
};
for (const [prop, value] of Object.entries(person)) {
console.log(prop, value);
}
// 'name', 'John Smith'
// 'job', 'agent'
Object.entries(person)
返回一個鍵和值的元組數組:[[''name','John Smith'],['job','agent']]
。然后,使用 for...of
循環遍歷數組,並將每個元組解構為 const [prop,value]
。
7. 遍歷DOM集合
你可能知道 HTMLCollection
令人沮喪。主要是因為 HTMLCollection
是一個類數組的對象(而不是常規數組),所以我們無法使用數組的方法。
例如,每個 DOM
元素的 children
屬性都是 HTMLCollection
。好在 for...of
可以在類似數組的對象上進行迭代,因此我們可以輕松地迭代 children
:
const children = document.body.children;
for (const child of children) {
console.log(child); // logs each child of <body>
}
此外,for...of
可以迭代 NodeList
集合(可迭代)。例如,函數 document.querySelectorAll(query)
返回一個 NodeList
:
const allImages = document.querySelectorAll('img');
for (const image of allImages) {
console.log(image); // log each image in the document
}
如果你想遍歷 DOM
中的不同種類的集合,那么 for...of
語句是一個不錯的選擇。
8. 性能
迭代大型數組時,for...of
的執行速度可能會比經典方法慢:
const a = [/* big array */];
for (let i = 0; i < a.length; i++) {
console.log(a[i]);
}
在每次迭代中調用迭代器比通過增加索引訪問的開銷更大。但是,這種細微差別在使用大型數組的應用程序中以及性能至關重要的應用程序中非常重要,不過這種情況很少發生。