I. Purpose
Used to iterate over arrays, similar to using for...in to iterate over objects.
for (var i = 0; i < arr.length; i++) works fine, and if you find it troublesome, you can use arr.forEach(). So what's the purpose of for...in?
for (var i = 0; i < arr.length; i++)is not concise enough
Too long, and length makes it easy to make mistakes. For example, a mistake I often make: for (var j = 0; j < arr[i].length; i++)
- forEach is not flexible enough
arr.forEach is not easy to use because you cannot break out or return through break or return
for...inis not suitable for iterating over arrays
for...in will iterate over custom properties and even prototype properties, index is a string rather than a number, and in some cases it doesn't even iterate in order
So we need a more convenient way to iterate over arrays, a method as simple and easy to use as using for...in to iterate over objects. That is for...of
II. Characteristics
1. Can Iterate Over Other Collections
Not only can it iterate over arrays, but also over other collections (iterable), including NodeList/arguments, strings, Map, Set, etc.
Example code is as follows:
// Array
var arr = [1, 2, 2, 4];
for (var val of arr) {
console.log(val);
}
// Array-like objects (arguments, NodeList)
(function() {
for (var val of arguments) {
console.log(val);
}
})(1, 2, 4);
// Set
var uniqueArr = new Set(arr); // Set automatically deduplicates
for (var val of uniqueArr) {
console.log(val);
}
// Map
var map = new Map([['a', 1], ['b', 2]]);
map.set('c', 3);
for (var [key, val] of map) {
console.log('map[' + key + '] = ' + val);
}
Note: Chrome47 doesn't support var [key, val] of map syntax, FF43 supports it, node --harmony v0.12.7 doesn't support destructuring expressions. If you want to experience ES6 comfortably, it's recommended to install think.js for node
2. Cannot Iterate Over Objects
for...of doesn't support iterating over objects, but you can add a [Symbol.iterator]() method to let other objects (such as jQuery objects) support for...of iteration. Objects with a [Symbol.iterator]() method are iterable.
How does it not support? Example code is as follows:
var obj = {
'a': 1,
'b': 2
}
for (var val of obj) {
console.log(val);
}
// Uncaught TypeError: is not a function
// Error, doesn't support iterating over objects, that's the responsibility of for...in
If you want to make custom objects support for...of iteration, you need to add a [Symbol.iterator]() method, as follows:
// General method to implement iterator
class MyIterator {
constructor(obj) {
this.obj = obj;
this.keys = Object.keys(obj);
this.index = 0;
}
// Implement iterator interface
[Symbol.iterator]() {
return this;
}
next() {
var val = this.obj[this.keys[this.index]];
if (this.index === this.keys.length) {
return {
done: true
}
}
else {
this.index++;
return {
done: false,
value: val
}
}
}
}
// Test
var _obj = {
a: 2,
b: 1
}
for (var val of new MyIterator(_obj)) {
console.log(val);
}
Wait, what is Symbol? Symbol has a significant background: Symbol is the 7th primitive type in js (the original 6 types are null, undefined, Number, Boolean, Object, String), not a string nor an object. Evidence is as follows:
typeof Symbol() === 'symbol'
The Symbol.iterator appearing in the code above is a built-in function name (of Symbol type, while general function names are String). When executing a for...of statement, the interpreter looks for a method named Symbol.iterator on the object after of. That method executes and returns an iterator object iterator. After getting iterator, naturally .next(), .next() until done. The process is similar to:
// Array
var arr = [1, 2, 2, 4];
// for (var val of arr) {
// console.log(val);
// }
var iter = arr[Symbol.iterator]();
while (true) {
var res = iter.next();
if (res.done) {
break;
}
else {
var val = res.value;
console.log(val);
}
}
Of course, our functionality is too weak. The power of for...of lies in supporting break, continue, return, just like the classic for loop.
3. Supports Custom Iterators
Actually, we've already implemented a custom iterator above, but it seems troublesome. Using this might as well be death. The people who制定 ES6 standards naturally thought of this. Combined with generator syntax (function* + yield), it's easy to implement custom iterators to support for...of iteration:
var obj = {
'a': 1,
'b': 2
}
// Generator implements iterator
obj[Symbol.iterator] = function*() {
for (var key in this) {
yield this[key];
}
}
for (var val of obj) {
console.log(val);
}
Implement an iterator in seconds, much more convenient now.
P.S. Actually, our MyIterator is strictly speaking a generator, a (iterator) generator.
III. Summary
ES5 introduced forEach, but actually it's not very useful. Many times we have to replace written forEach with classic for loops... forEach is not as flexible as every, but every creates a new array...
Well, now we have for...of, goodbye forEach.
References
- "ES6 in Depth": Free e-book provided by InfoQ Chinese Station
No comments yet. Be the first to share your thoughts.