JavaScript类数组和可迭代对象的实现原理详解

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属性,返回一个迭代器对象即可。