Iterables
JavaScript Iterables
The protocol of iterations enables JavaScript objects to define and customize their iteration behavior like what values are looped over in for…of. (迭代协议使JavaScript对象能够定义和自定义其迭代行为,例如for… of中循环的值。)
Arrays are considered iterable. But, there are also other iterables (for instance, strings). (数组被认为是可迭代的。但是,还有其他可迭代项(例如,字符串)。)
Describing Symbol.iterator
Describing Symbol.iterator (描述Symbol.iterator)
Let’s try to create an iterable. (让我们尝试创建一个可迭代的。)
For instance, there is an object, which is not an array but can be suitable for for…of. (例如,有一个对象,它不是数组,但可以适用于for… of。)
Let’s see an example of a range object, representing an interval of numbers:
let range = {
from: 0,
to: 10
};
// We want the for..of to work:
// for(let num of range) ... num=0,1,2,3,4,5,6,7,8,9,10
For transforming the range into iterable, it is necessary to add a method to the object called Symbol.iterator. (为了将范围转换为可迭代,有必要向名为Symbol.iterator的对象添加一个方法。)
These are the steps:
Once for..of begins, the method is called once. It should return an iterator. Then, for..of can operate with the returned object. Once, for..of expects the next value, next() is called on that object. Its result should include the {done: Boolean, value: any} form.
The full performance for the range with remarks will look as follows:
let range = {
from: 0,
to: 5
};
// 1. called to for..of initially calls this
range[Symbol.iterator] = function () {
//returns an iterator object:
// 2.for..of works only with this iterator, querying it for next values
(//2.for.. of仅适用于此迭代器,查询其下一个值)
return {
currentValue: this.from,
lastValue: this.to,
// 3. next() is called at each iteration with a for..of loop
(//3. next ()在每次迭代时使用for.. of循环调用)
next() {
// 4. it should return the value as an object {done:.., value :...}
if (this.currentValue <= this.lastValue) {
return {
done: false,
value: this.currentValue++
};
} else {
return {
done: true
};
}
}
};
};
for (let num of range) {
console.log(num); // 0, then 1, 2, 3, 4, 5
}
The most essential feature of iterables is the segregation of concerns. It considers that the iterator object is distinct from the ones that it iterates over. It is possible to merge them using the range like an iterator for making the code easier, as shown below:
let range = {
from: 0,
to: 5,
[Symbol.iterator]() {
this.currentValue = this.from;
return this;
},
next() {
if (this.currentValue <= this.to) {
return {
done: false,
value: this.currentValue++
};
} else {
return {
done: true
};
}
}
};
for (let num of range) {
console.log(num); // 0, then 1, 2, 3, 4, 5
}
The main disadvantage of this case is that it is not possible to have two for..of that run over the object at the same time. (这种情况的主要缺点是,不可能同时有两个for.. of在物体上运行。)
There can be infinite iterators. For example, you can transform range into infinite with range.to = Infinity. There is an alternative option, too: you can create an iterable object, which generates an infinite sequence of pseudorandom numbers. So, no limitations are applied on next, and it may return more values. Consequently, the for..of loop will also become endless over an iterable like this. But it can be stopped with break.
Strings as Iterables
Strings as Iterables (字符串作为迭代对象)
The most widely used iterables are strings and arrays. (最广泛使用的可迭代对象是字符串和数组。)
The for..of may loop over its characters, in case of a string:
for (let char of "w3cdoc") {
// triggers 6 times: once for each character
console.log(char); // W, then 3, then D, then o, then c, then s
}
It will work properly, like this:
let str = 'ȬẂ';
for (let char of str) {
console.log(char); // Ȭ, and then Ẃ
}
Array-likes and Iterables
Array-likes and Iterables (Array-likes和Iterables)
In this paragraph, we will look through the terms iterables and array-likes. They may seem similar but are very different. (在本段中,我们将查看术语iterables和array-likes。它们可能看起来很相似,但却截然不同。)
Array-likes are considered objects that contain indexes and length. Iterables are objects that are capable of implementing the Symbol.iterator method. This method was described above. (类数组被认为是包含索引和长度的对象。 迭代表是能够实现Symbol.iterator方法的对象。 该方法如上所述。)
While using JavaScript, you can meet a lot of array-lie and iterable objects. For instance, strings can both be iterables and array-likes. On the contrary, an iterable can never become an array-like, and vice versa. (使用JavaScript时,您可以遇到许多数组谎言和可迭代的对象。 例如,字符串既可以是可迭代的,也可以是类似于数组的。 相反,可迭代对象永远不可能成为类似数组的对象,反之亦然。)
Let’s check out an example of an object that is an array-like but not an iterable:
let arrLike = { // has indexes and length => array-like
0: "Welcome",
1: "to",
2: "w3cdoc",
length: 3
};
// Error (no Symbol.iterator)
for (let item of arrLike) {
console.log(item);
}
As a rule, both iterables and array-likes are not arrays, as they don’t contain pop, push, and so on. (通常, iterables和array-likes都不是数组,因为它们不包含pop、push等。)
Describing Array-form
Describing Array-form (描述Array-form)
Array-form is a generic method used for taking an iterable or array-like value and making a real array from it. Afterward, array methods can be called on it. Here is an example of using array-form:
let arrLike = {
0: "Welcome",
1: "to",
2: "w3cdoc",
length: 3
};
let arr = Array.from(arrLike); // (*)
console.log(arr.pop()); // w3cdoc (method operates)
At the (*) line, Array.from accepts the object, examining whether it’s an array-like or an iterable, then creates a new array, copying all the items into it. Let’s see how the same takes place with an iterable:
let range = {
from: 0,
to: 5,
[Symbol.iterator]() {
this.currentValue = this.from;
return this;
},
next() {
if (this.currentValue <= this.to) {
return {
done: false,
value: this.currentValue++
};
} else {
return {
done: true
};
}
}
};
//confirming that the range is obtained from the above example
let arr = Array.from(range);
console.log(arr); // 0,1,2,3,4,5 ;array toString conversion works
The full syntax of Array.from looks like this:
Array.from(obj[, mapFunc, thisArg])
The second argument mapFn is a function, which may be attached to every element before adding it to the array. the third argument thisArg allows setting this for that. Here is an example:
let range = {
from: 0,
to: 5,
[Symbol.iterator]() {
this.currentValue = this.from;
return this;
},
next() {
if (this.currentValue <= this.to) {
return {
done: false,
value: this.currentValue++
};
} else {
return {
done: true
};
}
}
};
// provided that the range is taken from the above example
// square of each number
let arr = Array.from(range, num => num * num);
console.log(arr); // 0,1,4,9,16,25
Now, let’s turn a string into an array of characters with the help of Array.from:
let str = 'ȬẂ';
// splits the str into an array of characters
let chars = Array.from(str);
console.log(chars[0]); // Ȭ
console.log(chars[1]); // Ẃ
console.log(chars.length); // 2
On the contrary to the str.split method, it depends on the iterable nature of the string. (与str.split方法相反,它取决于字符串的可迭代性。)
It acts the same as:
let str = 'ȬẂ';
let charsArr = []; // Array.from internally does the same loop
for (let char of str) {
charsArr.push(char);
}
console.log(charsArr);
Summary
Summary (概要)
Iterables are the objects that may be used in the for..of. (迭代对象是可以在for.. of中使用的对象。)
As a rule, they should perform the Symbol.iterator method. This method is, generally, called automatically by for..of. But, it can also be done directly. Objects, having indexed properties, and length are considered array-like. They can also include other properties and methods, but they are not real arrays. For transforming them into arrays, you can use the Array.from(obj[, mapFn, thisArg]) method. (通常,它们应执行Symbol.iterator方法。 此方法通常由for.. of自动调用。 但是,也可以直接完成。 具有索引属性和长度的对象被视为类似于数组。 它们还可以包括其他属性和方法,但它们不是真实数组。 要将它们转换为数组,可以使用Array.from (obj [, mapFn, thisArg])方法。)
The mapFn and thisArg arguments allow applying a function to each of them. (MapFn和thisArg参数允许对它们中的每一个应用一个函数。)