您现在的位置是:网站首页 > 类数组对象文章详情
类数组对象
陈川
【
JavaScript
】
46161人已围观
3842字
在 JavaScript 中,类数组对象是一种常见的结构,它看起来像数组,但并非真正的数组。这类对象通常具有数字索引和 length
属性,但缺乏数组的原生方法。理解类数组对象的特性和转换方式,能更灵活地处理数据。
什么是类数组对象
类数组对象是具有以下特征的对象:
- 使用数字作为属性名(如
0
,1
,2
...) - 拥有
length
属性 - 不具备数组方法(如
push
,pop
,slice
等)
典型的例子包括:
- 函数内的
arguments
对象 - DOM 操作返回的
NodeList
(如document.querySelectorAll
的结果) - 字符串(每个字符可通过索引访问)
// 典型的类数组对象
const arrayLike = {
0: 'a',
1: 'b',
2: 'c',
length: 3
};
类数组对象的检测方法
判断一个对象是否为类数组对象可以通过以下方式:
function isArrayLike(obj) {
// 排除 null/undefined 和非对象类型
if (obj == null || typeof obj !== 'object') return false;
// 检查 length 属性
const length = obj.length;
return typeof length === 'number' &&
length >= 0 &&
length % 1 === 0 &&
length <= Number.MAX_SAFE_INTEGER;
}
console.log(isArrayLike([1,2,3])); // true
console.log(isArrayLike({0:'a', length:1})); // true
console.log(isArrayLike('abc')); // false(字符串是原始类型)
类数组对象转为真实数组
由于类数组对象无法直接使用数组方法,通常需要先进行转换:
方法1:Array.from()
ES6 引入的最简单方式:
const arrayLike = {0: 'a', 1: 'b', length: 2};
const realArray = Array.from(arrayLike);
console.log(realArray); // ['a', 'b']
方法2:扩展运算符
适用于可迭代对象(如 arguments
、NodeList
):
function example() {
return [...arguments];
}
console.log(example(1,2,3)); // [1, 2, 3]
方法3:Array.prototype.slice.call()
传统浏览器兼容方案:
const nodeList = document.querySelectorAll('div');
const nodeArray = Array.prototype.slice.call(nodeList);
方法4:手动遍历
通用性最强的方案:
function convertArrayLike(obj) {
const arr = [];
for (let i = 0; i < obj.length; i++) {
arr[i] = obj[i];
}
return arr;
}
常见类数组对象示例
arguments 对象
函数内部自动生成的类数组对象:
function sum() {
// 旧版转换方式
const args = Array.prototype.slice.call(arguments);
return args.reduce((a,b) => a + b, 0);
}
// ES6 更推荐使用剩余参数
function modernSum(...args) {
return args.reduce((a,b) => a + b, 0);
}
DOM 集合
// NodeList
const divs = document.querySelectorAll('div');
divs.forEach(div => console.log(div)); // 现代浏览器已为 NodeList 添加 forEach
// HTMLCollection
const forms = document.forms; // HTMLCollection
Array.from(forms).forEach(form => {
console.log(form.method);
});
字符串的类数组特性
字符串虽然原始类型,但具有类似数组的访问方式:
const str = 'hello';
console.log(str[1]); // "e"
console.log(Array.from(str)); // ["h", "e", "l", "l", "o"]
实现自定义类数组对象
通过定义索引属性和 length 属性即可创建:
const customArrayLike = {
// 必须的属性
0: 'first',
1: 'second',
length: 2,
// 可选的迭代器(使对象可迭代)
[Symbol.iterator]: function*() {
for(let i = 0; i < this.length; i++) {
yield this[i];
}
},
// 自定义方法
join: function(separator = ',') {
return Array.from(this).join(separator);
}
};
console.log(customArrayLike[1]); // "second"
console.log([...customArrayLike]); // ["first", "second"]
console.log(customArrayLike.join('|')); // "first|second"
类数组对象的性能考量
在某些场景下,直接操作类数组对象比转换为真实数组更高效:
// 更高效的方式(避免创建新数组)
function sumArguments() {
let sum = 0;
for(let i = 0; i < arguments.length; i++) {
sum += arguments[i];
}
return sum;
}
// 需要多次访问时转换为数组更合适
function processElements() {
const elements = [...arguments];
// 多次操作...
elements.filter(x => x > 0).map(x => x * 2);
}
与类型化数组的区别
类型化数组(如 Uint8Array
)虽然也是类数组结构,但有本质不同:
特性 | 类数组对象 | 类型化数组 |
---|---|---|
内存分配 | 普通对象 | 连续内存缓冲区 |
元素类型 | 任意类型 | 特定数值类型 |
性能 | 较低 | 接近原生数组 |
方法支持 | 无数组方法 | 有部分数组方法 |
// 类型化数组示例
const typedArray = new Uint8Array([1, 2, 3]);
console.log(typedArray.map(x => x * 2)); // Uint8Array [2, 4, 6]