技术知识文章集合TECHNICAL ARCHIVE · 457 DOCUMENTS

显示模式

登录
ARCHIVE DOCUMENTJS

判断一个对象是否为数组的 4 种方法

所属馆藏
JavaScript
文件格式
Markdown
原始路径
JavaScript/83-判断一个对象是否为数组的4种方法
本文目录6 个章节
  1. 方法一:判断原型
  2. 方法二:判断构造函数或使用 instanceof
  3. 方法三:Object.prototype.toString
  4. 方法四:Array.isArray()(推荐)
  5. 类数组对象不是数组
  6. 总结

判断一个对象是否为数组的 4 种方法

Category(分类): JavaScript Status: 已整理

在 JavaScript 中,typeof []typeof {} 都是 "object",所以 typeof 不能直接区分数组和普通对象:

console.log(typeof []) // object
console.log(typeof {}) // object

下面保留原文讨论的四类方法,并补充它们的边界。生产代码通常优先使用 Array.isArray()

方法一:判断原型

const object = {}
const array = []

console.log(Object.getPrototypeOf(object) === Object.prototype) // true
console.log(Object.getPrototypeOf(array) === Array.prototype) // true

这种方法只适合分析一个由当前 realm 创建、且原型没有被修改的普通数组。对象的 [[Prototype]] 可以被改变,跨 iframe 或其他 realm 的数组也有不同的 Array.prototype

const object = {}
Object.setPrototypeOf(object, Array.prototype)

// 这只能说明 object 的原型被改成了 Array.prototype,
// 不能说明它真的具有数组的内部槽。
console.log(Object.getPrototypeOf(object) === Array.prototype) // true
console.log(Array.isArray(object)) // false

Object.setPrototypeOf 还可能影响性能,不应为了类型检测修改对象原型。

方法二:判断构造函数或使用 instanceof

const object = {}
const array = []

console.log(array.constructor === Array) // true(未被篡改时)
console.log(array instanceof Array) // true(当前 realm 中)

这种方法容易受两个问题影响:

  1. constructor 属性可以被覆盖,不能作为可靠的品牌检查。
  2. instanceof Array 依赖原型链和当前 realm。来自 iframe 的数组,可能不是当前窗口的 Array 实例。
// 浏览器中示例:跨 iframe 的数组
const iframe = document.createElement('iframe')
document.body.append(iframe)
const foreignArray = new iframe.contentWindow.Array(1, 2, 3)

console.log(foreignArray instanceof Array) // false
console.log(Array.isArray(foreignArray)) // true

instanceof 可以通过 Symbol.hasInstance 自定义,因此它适合判断“是否属于某个构造器体系”,不适合替代数组品牌检查。

方法三:Object.prototype.toString

const tag = Object.prototype.toString

console.log(tag.call([])) // [object Array]
console.log(tag.call({})) // [object Object]
console.log(tag.call(null)) // [object Null]

直接调用 array.toString() 得到的是数组元素拼接后的字符串,例如 [].toString() 是空字符串,并不是类型标签。因此需要调用 Object.prototype.toString.call(value)

这个方法比读取 constructor 稳定,也通常能识别跨 realm 的数组;但对象可以通过 Symbol.toStringTag 影响标签,现代代码仍然应优先使用 Array.isArray()

const fakeArray = {
  [Symbol.toStringTag]: 'Array',
}

console.log(Object.prototype.toString.call(fakeArray)) // [object Array]
console.log(Array.isArray(fakeArray)) // false

方法四:Array.isArray()(推荐)

console.log(Array.isArray([])) // true
console.log(Array.isArray({})) // false
console.log(Array.isArray('array')) // false
console.log(Array.isArray({ length: 0 })) // false

Array.isArray() 使用 ECMAScript 的 IsArray 语义进行品牌判断,不依赖当前 realm 的原型、constructorSymbol.toStringTag,因此是判断数组最可靠、最清晰的方式。

const object = {}
const array = []
Object.setPrototypeOf(object, Array.prototype)
Object.getPrototypeOf(object).constructor = Array

console.log(Array.isArray(object)) // false
console.log(Array.isArray(array)) // true

类数组对象不是数组

argumentsNodeListHTMLCollection 以及手动创建的 { 0: 'a', length: 1 } 可能是可迭代或类数组对象,但它们不是数组:

function inspect() {
  console.log(Array.isArray(arguments)) // false
  console.log(Array.from(arguments)) // ['a', 'b']
}

inspect('a', 'b')

需要数组方法时,可以使用 Array.from(value) 或展开语法(前提是对象可迭代):

const arrayLike = { 0: 'a', 1: 'b', length: 2 }
const array = Array.from(arrayLike)
console.log(array) // ['a', 'b']

总结

方法主要问题建议
Object.getPrototypeOf(value) === Array.prototype原型可修改、跨 realm 失效只适合原理演示
value.constructor === Array / instanceof Array构造函数可覆盖、instanceof 受原型链和 realm 影响只在明确的当前 realm 场景使用
Object.prototype.toString.call(value)Symbol.toStringTag 可影响标签兼容旧代码时可用
Array.isArray(value)语义清晰,支持跨 realm首选

在深拷贝、参数校验、序列化或数组方法封装中,需要区分数组和对象时,优先写 Array.isArray(value)

原文参考:判断一个对象是否为数组的4种方法

参考资料:

457 DOCUMENTS · 10 COLLECTIONS
ARCHIVE SEARCH457 篇文章

SEARCH GUIDE

输入关键词开始搜索

支持搜索文章标题、所属分类和原始文档路径。

按分类浏览

10 COLLECTIONS