类数组结构是拥有length属性和数字键但非Array实例的对象,需通过非null、length为非负整数、所有索引键存在三条件识别,递归拷贝时转为真实数组。

什么是类数组结构
类数组对象是指拥有 length 属性 和若干数字键(如 0、1…),但并非真正 Array 实例的对象,比如 arguments、NodeList、HTMLCollection,或手动构造的 {0: 'a', 1: 'b', length: 2}。它们能用 for 循环遍历,但不能直接调用 map、slice 等数组方法。
手动递归识别类数组的关键判断条件
仅靠 typeof obj === 'object' 或 obj instanceof Array 不够——前者会把 null 和数组都判为 true,后者对类数组返回 false。应组合以下三点:
-
非 null 且为 object 类型(排除
null、原始值) -
存在
length属性,且为非负整数(Number.isInteger(obj.length) && obj.length >= 0) -
所有索引键(
0到length - 1)都存在于对象中(避免误判{length: 3, 0: 'x'}这种“稀疏伪类数组”)
递归拷贝时对类数组的特殊处理
一旦确认是类数组,就不再走普通对象分支,而是按索引顺序逐项递归拷贝,并最终返回一个真实数组(而非保留原对象结构)。这样既保证类型安全,又支持后续数组方法调用:
- 先创建空数组
const arr = [] - 用
for (let i = 0; i 遍历每个有效索引 - 对每一项调用同一递归函数拷贝(支持嵌套类数组、对象、Date、RegExp 等)
- 将结果 push 进新数组,最后返回该数组
注意:不使用 Array.from() 或扩展运算符,因目标是纯手动递归实现,需显式控制流程与类型判断。
完整判断 + 拷贝逻辑示例(精简核心)
以下是一个可直接复用的递归函数片段(省略 Date/RegExp 等其他类型,聚焦类数组):
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') return obj;
<p>// 类数组判断
if (obj.length !== undefined &&
Number.isInteger(obj.length) &&
obj.length >= 0 &&
Array.from({ length: obj.length }).every((_, i) => i in obj)) {
const arr = [];
for (let i = 0; i < obj.length; i++) {
arr[i] = deepClone(obj[i]);
}
return arr;
}</p><p>// 普通对象处理(非类数组)
const cloned = {};
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
cloned[key] = deepClone(obj[key]);
}
}
return cloned;
}</p>这个实现能正确区分 {0:'a',1:'b',length:2}(转为 ['a','b'])和 {length:2, name:'list'}(仍作为普通对象拷贝)。

















