The principle of the for...of loop
The for...of
loop is a new syntax introduced in ECMAScript 6 for traversing data structures. It is based on the iterator protocol and provides a unified way to handle iterable objects such as arrays, strings, Maps, and Sets. Compared to traditional for
loops and the forEach
method, it offers a more concise syntax and more powerful functionality.
Basic Syntax of for...of
Loop
The syntax of the for...of
loop is very simple:
for (const item of iterable) {
// Loop body
}
Here, iterable
can be any object that implements the iterable protocol. Below is a simple example of traversing an array:
const fruits = ['apple', 'banana', 'orange'];
for (const fruit of fruits) {
console.log(fruit);
}
// Output:
// apple
// banana
// orange
Iterator Protocol and Iterable Objects
The core of the for...of
loop lies in the iterator protocol. For an object to be iterable, it must implement the @@iterator
method, meaning it must have a Symbol.iterator
property. This property is a function that returns an iterator object.
The iterator object must implement a next()
method, which returns an object with value
and done
properties:
const arrayIterator = [1, 2, 3][Symbol.iterator]();
console.log(arrayIterator.next()); // { value: 1, done: false }
console.log(arrayIterator.next()); // { value: 2, done: false }
console.log(arrayIterator.next()); // { value: 3, done: false }
console.log(arrayIterator.next()); // { value: undefined, done: true }
Built-in Iterable Objects
Many built-in objects in ECMAScript 6 are iterable:
Arrays
const numbers = [10, 20, 30];
for (const num of numbers) {
console.log(num * 2);
}
// Output: 20, 40, 60
Strings
Each character in a string is iterated sequentially:
const greeting = 'Hello';
for (const char of greeting) {
console.log(char);
}
// Output: H, e, l, l, o
Maps and Sets
const map = new Map();
map.set('name', 'John');
map.set('age', 30);
for (const [key, value] of map) {
console.log(`${key}: ${value}`);
}
// Output: name: John, age: 30
const set = new Set([1, 2, 3]);
for (const item of set) {
console.log(item * 2);
}
// Output: 2, 4, 6
NodeList
DOM NodeLists are also iterable:
const divs = document.querySelectorAll('div');
for (const div of divs) {
div.style.color = 'red';
}
Differences from for...in
Loop
Although for...of
and for...in
have similar syntax, they are fundamentally different:
const arr = ['a', 'b', 'c'];
arr.foo = 'bar';
// for...in iterates over enumerable properties (including the prototype chain)
for (const key in arr) {
console.log(key); // Output: 0, 1, 2, foo
}
// for...of iterates over the values of iterable objects
for (const value of arr) {
console.log(value); // Output: a, b, c
}
Custom Iterable Objects
We can create our own iterable objects:
const myIterable = {
[Symbol.iterator]() {
let step = 0;
return {
next() {
step++;
if (step <= 3) {
return { value: `step ${step}`, done: false };
}
return { value: undefined, done: true };
}
};
}
};
for (const item of myIterable) {
console.log(item);
}
// Output:
// step 1
// step 2
// step 3
Early Termination of Iteration
The for...of
loop can be terminated early using break
, return
, or throw
:
function* generateNumbers() {
yield 1;
yield 2;
yield 3;
}
for (const num of generateNumbers()) {
if (num > 2) break;
console.log(num);
}
// Output: 1, 2
Asynchronous Iteration
ES2018 introduced asynchronous iterators and the for await...of
syntax:
async function* asyncGenerator() {
yield await Promise.resolve(1);
yield await Promise.resolve(2);
yield await Promise.resolve(3);
}
(async function() {
for await (const num of asyncGenerator()) {
console.log(num);
}
})();
// Output: 1, 2, 3
Destructuring Assignment with for...of
for...of
can be combined with destructuring assignment:
const users = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 },
{ name: 'Charlie', age: 35 }
];
for (const { name, age } of users) {
console.log(`${name} is ${age} years old`);
}
Performance Considerations
In most modern JavaScript engines, the performance of for...of
loops is close to that of traditional for
loops. However, in performance-critical code, caution is still advised:
// Traditional for loop
for (let i = 0; i < array.length; i++) {
// Potentially faster
}
// for...of loop
for (const item of array) {
// More concise but slightly slower
}
Common Use Cases
Traversing Array-like Objects
function sum() {
let total = 0;
for (const num of arguments) {
total += num;
}
return total;
}
console.log(sum(1, 2, 3)); // 6
Traversing Generator Functions
function* fibonacci() {
let [prev, curr] = [0, 1];
while (true) {
[prev, curr] = [curr, prev + curr];
yield curr;
}
}
for (const num of fibonacci()) {
if (num > 1000) break;
console.log(num);
}
Getting Both Index and Value
Using the entries()
method:
const colors = ['red', 'green', 'blue'];
for (const [index, color] of colors.entries()) {
console.log(`Color at position ${index} is ${color}`);
}
Comparison with Other Iteration Methods
Comparison with forEach
// forEach
[1, 2, 3].forEach((item) => {
console.log(item);
});
// for...of
for (const item of [1, 2, 3]) {
console.log(item);
}
The advantage of for...of
is that it allows the use of break
and continue
, which forEach
does not.
Comparison with for
Loop
// Traditional for loop
for (let i = 0; i < array.length; i++) {
if (array[i] === 'stop') break;
console.log(array[i]);
}
// for...of loop
for (const item of array) {
if (item === 'stop') break;
console.log(item);
}
for...of
is more concise, but traditional for
loops offer more flexible control over indices.
Browser Compatibility and Transpilation
Although modern browsers support for...of
, older environments may require transpilation tools like Babel:
// Before transpilation
for (const item of iterable) {
// ...
}
// After transpilation
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = iterable[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var item = _step.value;
// ...
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return != null) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn