数组内置了一个迭代方法来用于迭代数组的内容,例如for...of就是用到了迭代方法。不单单是Array,像String也同样内置了迭代方法,理论上讲,Javascript为所有对象都提供了迭代机制,对于普通的对象,我们需要手动实现迭代方法。
Symbol.iterator
要使对象可迭代,我们需要实现Symbol.iterator方法。当我们使用for...of时,Javascript会默认去调用对象的Symbol.iterator方法来得到一个迭代对象,然后再调用迭代对象里的next()方法来获得对象(也就是要被迭代的对象,如Array)下一个值。Javascript规定Symbol.iterator方法必须返回一个对象,next()返回的对象格式为{done: Boolean, value: any},其中done=true表示迭代结束,否则value为下一个新值,例子如下:
let range = {
from: 1,
to: 5
};
// 1. call to for..of initially calls this
range[Symbol.iterator] = function() {
// 2. ...it returns the iterator:
return {
current: this.from,
last: this.to,
// 3. next() is called on each iteration by the for..of loop
next() {
// 4. it should return the value as an object {done:.., value :...}
if (this.current <= this.last) {
return { done: false, value: this.current++ };
} else {
return { done: true };
}
}
};
};
// now it works!
for (let num of range) {
alert(num); // 1, then 2, 3, 4, 5
}
为了提高代码的可读性,我们可以在Symbol.iterator方法里面返回this来指向被迭代对象本身,再在被迭代对象里添加next()方法,例如:
let range = {
from: 1,
to: 5,
[Symbol.iterator]() {
this.current = this.from;
return this;
},
next() {
if (this.current <= this.to) {
return { done: false, value: this.current++ };
} else {
return { done: true };
}
}
};
for (let num of range) {
alert(num); // 1, then 2, 3, 4, 5
}
String也是可迭代的
String也是可迭代的,由于它默认实现了迭代方法,故我们可以直接使用for...of,例如:
for (let char of "test") {
alert( char ); // t, then e, then s, then t
}
对于特殊字符串也一样,例如:
let str = '??';
for (let char of str) {
alert( char ); // ?, and then ?
}
显式调用迭代器iterator
通常我们都是使用for...of来隐示调用迭代器,当然我们也可以显示地调用,例如:
let str = "Hello";
// does the same as
// for (let char of str) alert(char);
let iterator = str[Symbol.iterator]();
while (true) {
let result = iterator.next();
if (result.done) break;
alert(result.value); // outputs characters one by one
}
类数组对象
什么是类数组对象呢?类数组对象就是拥有索引号和length的对象,它的格式看起来像数组,可实际上它只是普通的对象,例如:
let arrayLike = { // has indexes and length => array-like
0: "Hello",
1: "World",
length: 2
};
// Error (no Symbol.iterator)
for (let item of arrayLike) {}
类数组对象由于没有实现迭代方法,故不能使用迭代功能
Array.from()
Array.from()可以将一个类数组对象转换为一个数组,其内部原理是新建一个空数组对象,然后将类数组对象的属性一个个复制到空数组对象里,同时实现迭代方法,最后返回该新数组,使用例子如下:
let arrayLike = {
0: "Hello",
1: "World",
length: 2
};
let arr = Array.from(arrayLike); // (*)
alert(arr.pop()); // World (method works)
Array.from()的具体语法如下:
Array.from(obj[, mapFn, thisArg])
其中mapFn是一个可选的函数参数,在把类数组属性复制到新数组对象的过程对该属性进行操作,thisArg用于为mapFn绑定this,例如:
// assuming that range is taken from the example above
// square each number
let arr = Array.from(range, num => num * num);
alert(arr); // 1,4,9,16,25
String也可以通过Array.from()转换数组对象,例如:
let str = '??';
// splits str into array of characters
let chars = Array.from(str);
alert(chars[0]); // ?
alert(chars[1]); // ?
alert(chars.length); // 2
它内部原理其实也就是下面的例子:
let str = '??';
let chars = []; // Array.from internally does the same loop
for (let char of str) {
chars.push(char);
}
alert(chars);