1. 什么是类数组和可迭代对象
在了解JavaScript类数组和可迭代对象的实现原理之前,我们先要明确什么是类数组和可迭代对象。
类数组是指具有length属性且属性名为非负整数的对象,所以类数组对象并非真正的数组对象,但是它们拥有许多数组对象的特性。最常见的类数组对象是DOM元素列表。
可迭代对象是指具有Symbol.iterator属性的对象,该属性是一个函数,返回值为迭代器(Iterator)对象。可迭代对象可以用for...of循环遍历。
2. 类数组对象的实现原理
2.1 类数组对象和数组对象的区别
在JavaScript中,类数组对象和数组对象虽然有一些相似的地方,但是它们之间还是有一些区别的。
数组对象是一种有序的、具有一定长度的、能够通过索引访问元素的对象类型。
let arr = [1, 2, 3];
console.log(arr[0]); // 1
类数组对象是一种对象类型,它有一个length属性,通过下标访问元素,但是它并没有数组对象的一些方法和属性,比如push、pop、slice等。
let obj = {
0: 'a',
1: 'b',
2: 'c',
length: 3
};
console.log(obj[0]); // "a"
console.log(obj.length); // 3
2.2 类数组对象与数组对象的转换
由于类数组对象不是真正的数组对象,因此它们缺少很多数组对象自带的方法和属性,所以我们需要将其转换成数组对象。
将类数组对象转换成数组对象的方法有很多,比如slice、Array.from、扩展运算符等。
let obj = {
0: 'a',
1: 'b',
2: 'c',
length: 3
};
let arr = Array.prototype.slice.call(obj);
console.log(arr); // ["a", "b", "c"]
将数组对象转换成类数组对象也很简单,只需要去掉一些属性即可。
let arr = [1, 2, 3];
let obj = {
0: 1,
1: 2,
2: 3,
length: 3
}
3. 可迭代对象的实现原理
3.1 可迭代对象特征
可迭代对象有一个Symbol.iterator属性,它是一个函数,返回一个迭代器对象,该对象有一个next方法,用来返回一个包含value和done属性的对象。如果done为true,则表示迭代器已经完成了迭代,value属性表示最后一次迭代返回的值;否则迭代器还没有完成迭代,value属性表示下一个要迭代的值。
let iter = [1, 2, 3][Symbol.iterator]();
console.log(iter.next()); // {value: 1, done: flase}
console.log(iter.next()); // {value: 2, done: flase}
console.log(iter.next()); // {value: 3, done: flase}
console.log(iter.next()); // {value: undefined, done: true}
3.2 自定义可迭代对象
我们也可以自定义可迭代对象,只需要为对象添加Symbol.iterator属性即可。
let range = {
from: 1,
to: 5,
[Symbol.iterator]() {
let current = this.from;
let last = this.to;
return {
next() {
if (current <= last) {
return {value: current++, done: false};
} else {
return {done: true};
}
}
};
}
};
for (let item of range) {
console.log(item);
}
// 1
// 2
// 3
// 4
// 5
在上面的例子中,我们为range对象添加了Symbol.iterator属性,它返回一个迭代器对象,该对象有一个next方法,每次执行next方法时会返回一个包含value和done属性的对象。在for...of循环中,我们已经可以像遍历数组一样遍历该对象的所有元素了。
4. 总结
在JavaScript中,类数组对象和可迭代对象都是非常常见的对象类型。类数组对象虽然和数组对象有些区别,但是我们可以通过一些方法将其转换成数组对象,让其拥有更多的方法和属性。可迭代对象则具有Symbol.iterator属性,可以通过for...of循环遍历其所有元素。我们也可以自定义可迭代对象,只需要为对象添加Symbol.iterator属性,返回一个迭代器对象即可。