先记住这个答案
Array.isArray 检查值是否具备数组的内部身份,而 instanceof Array 通常沿原型链查当前这个构造器的 prototype。不同 iframe 或 realm 拥有各自的 Array 构造器,外部数组的原型不一定经过当前 Array.prototype,因此 instanceof 可能为 false,Array.isArray 仍能识别。反过来,普通对象若把原型设成 Array.prototype,可以通过普通 instanceof 检查,却不是真数组。Array.isArray 也不把 TypedArray 当普通数组;确认容器身份后,还需要单独验证元素类型、长度和业务结构。
- Array.isArray 判断数组身份而非外观
- 跨 realm 使用不同 Array.prototype
- 数组身份不等于数组内容已通过校验
用伪造原型对象说明检查目标不同
下面创建一个以 Array.prototype 为原型的普通对象。它能让普通 instanceof 的原型链检查成立,但 Array.isArray 仍返回 false,说明继承方法和拥有数组内部身份并不是同一回事。
TypedArray 可以按索引访问并支持许多序列操作,却有固定元素表示等不同规则,所以不会被识别为普通数组。接口若同时接受这些类型,应分别定义支持条件,而不是放宽到任何带 length 的对象。
const real = [1, 2];
const imitation = Object.create(Array.prototype);
const typed = new Uint8Array([1, 2]);
console.log(Array.isArray(real), real instanceof Array);
console.log(Array.isArray(imitation), imitation instanceof Array);
console.log(Array.isArray(typed), typed instanceof Array);查看输出与解释
true true
false true
false false普通数组同时满足两种检查,伪造原型只满足原型链条件,TypedArray 则不属于普通 Array。示例没有改变全局原型,测试对象在当前隔离执行环境里独立创建。
跨 realm 需要理解构造器来自哪里
同源 iframe 的脚本创建数组后,把它交给父页面时,数组仍保留所属 realm 的原型关系。父页面拿自己的 Array 做 instanceof,可能找不到同一个原型对象,因此得到 false。
这个现象不表示数组损坏,也不需要为了检测先复制所有元素。可以用 Array.isArray 识别容器,再根据接口校验内容;访问跨源 iframe 仍受浏览器同源规则约束,类型方法不能绕过访问限制。
身份检查只是输入验证的第一步
Array.isArray 为 true 的值仍可能包含空位、错误元素类型、过大长度或恶意构造的对象成员。业务解析应继续检查数据规模、字段和允许的值,不能把一个布尔结果当作完整可信边界。
若代码只是消费可迭代对象,强制要求数组可能又过于严格。应先确定真正需要数组随机索引与长度语义,还是只需要顺序读取,然后选择品牌检查、协议检查或显式转换。
容易答错的地方
- 用 instanceof Array 作为跨窗口通用判断
- 不同 realm 的构造器和原型不同,真实数组可能被误判。通用数组身份判断应使用 Array.isArray,并在需要跨环境的接口测试外部数组,而不只验证当前页面字面量。
- 确认是数组后直接相信所有成员
- 容器身份不保证元素类型、无空位或长度安全。应继续执行与业务输入契约相符的检查,必要时返回明确错误,避免深层代码在更远位置才因错误字段失败。
面试官还会怎么问?
Object.prototype.toString 更可靠吗?
它可用于观察常见内建标签,但 Symbol.toStringTag 等机制能影响结果,不能作为数组身份检查的替代保证。针对数组应使用专门方法,其他类型则需要结合具体 API 契约判断。
Proxy 包装数组还是数组吗?
正常代理包装真实数组时,Array.isArray 通常仍识别为数组,但已撤销代理等情况可能抛错。外部未知值的错误处理应依据实际接口要求,不能把所有对象检查都假设成绝不会失败。
Array.prototype 自己算数组吗?
它本身也是数组,这说明 Array.isArray 判断的是内部身份,并不要求值由当前代码调用 new Array 创建。面试可以用这个边界检验理解,但业务校验仍应关注实际输入内容和允许范围。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。