如何判断 JS 数组

3,753 阅读2分钟

配图源自 Freepik

原文链接

先说结论

一律使用 ES5 提供的方法:

Array.isArray()

非要降级考虑,那就:

function isArray(val) {
  return Object.prototype.toString.call(val) === '[object Array]'
}

其原理是通过 Object.prototype.toString() 判断对象的内部属性 [[Class]] 是否为 "Array"

其他方法的缺陷

typeof

要是 Function、String、Number、Undefined 等类型来说,它完全可以胜任。

但判断 Array 类型不行的,因此不能用它来判断一个值是否为数组。

const arr = []
console.log(typeof arr) // "object"

// 同样地
console.log(typeof null) // "object"
console.log(typeof {}) // "object"

instanceof 和 constructor

instanceof 用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上,比如:

const arr = []
console.log(arr instanceof Array) // true

使用 constructor 判断类型,比如:

const arr = []
console.log(arr.constructor === Array) // true

// 其他
// arr.__proto__.constructor === Array // true
// arr.__proto__ === Array.prototype // true

在某些情况下是不准确的,比如:

function Fn() {}
Fn.prototype = new Array()

const instance = new Fn()

console.log(instance.constructor === Fn) // false
console.log(instance.constructor === Array) // true
// 此时 instance 应该是一个普通对象,而非数组,使用 constructor 判断是不合适的

使用 instanceof 和 constructor 的局限性:

使用和声明都必须是在当前页面,比如父页面引用了子页面,在子页面中声明了一个 Array,将其赋值给父页面的一个变量,那么此时做原型链的判断:Array === object.constructor 得到的是 false,原因如下:

  1. Array 属于引用型数据,在传递过程中,仅仅是引用地址的传递。
  2. 每个页面的 Array 原生对象所引用的地址是不一样的,在子页面声明的Array 所对应的构造函数是子页面的 Array 对象;父页面来进行判断,使用的 Array 并不等于子页面的 Array

Categorizing values in JavaScript 一段原话: Array.isArray() exists because of one particular problem in browsers: each frame has its own global environment. An example: Given a frame A and a frame B (where either one can be the document). Code in frame A can pass a value to code in frame B. Then B code cannot use instanceof Array to check whether the value is an array, because its B Array is different from the A Array (of which the value could be an instance).

示例:

const iframe = document.createElement('iframe')
document.body.appendChild(iframe)

const xArray = window.frames[window.frames.length - 1].Array
const xarr = new xArray()
const arr = new Array()

// 不同页面,结果并非我们所预期的 true,而是 false
console.log(xarr instanceof Array) // false
console.log(xarr.constructor === Array) // false

// 同页面才是 true
console.log(arr instanceof Array) // true
console.log(arr.constructor === Array) // true