一、伪数组的定义与本质
定义:伪数组(Array-like Object)是具有 length 属性 和 索引元素,但不具备数组原生方法(如 push、forEach)的对象。
本质:
- 本质是对象,而非真正的数组实例(
instanceof Array === false
) - 常见于 DOM 节点集合(如
document.querySelectorAll
)、函数参数(arguments
)等
二、伪数组的典型特征
-
必备属性:
- 具有
length
属性(数值类型,代表元素个数) - 元素可通过索引(
0, 1, 2...
)访问
- 具有
-
缺失功能:
- 没有数组原型链上的方法(如
map
、filter
) - 调用数组方法会报错(如
obj.push(1)
会提示push is not a function
)
- 没有数组原型链上的方法(如
三、常见伪数组场景
场景 | 示例 | 特性 |
---|---|---|
函数参数 | arguments | 存储函数调用时的参数 |
DOM节点集合 | document.querySelectorAll('div') | 动态集合,元素增删会实时更新 |
字符串 | "abc" | 可通过索引访问字符,length=3 |
自定义对象 | {0: 'a', 1: 'b', length: 2} | 手动模拟伪数组结构 |
四、伪数组转真数组的方案
1. Array.from(ES6+,推荐)
const pseudoArray = {0: 1, 1: 2, 2: 3, length: 3};
const realArray = Array.from(pseudoArray); // [1, 2, 3]
// 带映射转换
const doubled = Array.from(pseudoArray, x => x * 2); // [2, 4, 6]
- 优势:支持映射函数,语法简洁,现代浏览器兼容
2. 扩展运算符(ES6+)
const domNodes = document.querySelectorAll('div');
const nodeArray = [...domNodes]; // 转为真数组
- 注意:需确保伪数组具有
length
属性且索引连续
3. Array.prototype.slice.call(兼容性方案)
function fn() {
const argsArray = Array.prototype.slice.call(arguments);
return argsArray.map(x => x * 2);
}
fn(1, 2, 3); // [2, 4, 6]
- 原理:利用
slice
方法将伪数组转为数组 - 兼容性:IE9+ 有效
五、问题
1. 问:伪数组和真数组的本质区别?
- 答:
- 原型链不同:真数组继承
Array.prototype
,拥有数组方法;伪数组是普通对象,原型为Object.prototype
; - 类型检测:
Array.isArray(真数组)
返回true
,伪数组返回false
。
- 原型链不同:真数组继承
2. 问:为什么 DOM 节点集合是伪数组?
- 答:
- 浏览器设计时,DOM 节点集合需要动态反映文档变化(如元素删除时自动更新),而数组是静态结构;
- 伪数组结构更轻量,无需实现数组的所有方法。
3. 问:如何判断一个对象是否为伪数组?
- 答:
function isArrayLike(obj) { // 1. 非null且为对象 2. 有length属性 3. length为非负整数 4. length不超过安全整数范围 return obj !== null && typeof obj === 'object' && 'length' in obj && Number.isInteger(obj.length) && obj.length >= 0 && obj.length <= Number.MAX_SAFE_INTEGER; }
4. 问:伪数组转数组的方案中,性能最好的是哪个?
- 答:
- 扩展运算符(...) 和 Array.from 性能相近,均优于
slice.call
; slice.call
需创建新数组并复制元素,开销略大;- 现代浏览器中推荐使用扩展运算符,代码更简洁。
- 扩展运算符(...) 和 Array.from 性能相近,均优于
六、伪数组的应用与最佳实践
-
函数参数处理:
// 不定长参数转数组 function sum() { const args = Array.from(arguments); return args.reduce((total, num) => total + num, 0); }
-
DOM操作优化:
// 伪数组转数组后使用数组方法 const images = [...document.querySelectorAll('img')]; images.filter(img => img.complete).forEach(img => console.log('已加载'));
-
自定义伪数组:
// 创建一个可遍历的伪数组 const myArrayLike = { 0: 'a', 1: 'b', 2: 'c', length: 3, forEach: Array.prototype.forEach // 手动添加数组方法 }; myArrayLike.forEach(item => console.log(item)); // a b c