返回首页

JS 里怎么判断一个值是不是数组

Array.isArray、instanceof、Object.prototype.toString.call 的边界与选择。

发布 2018年9月09日 标签 #javascript #types

~/posts/is-array $ cat post.md

/ 语言 EN / 中文
/ 主题 / /

概览

判断一个值是不是数组的方法不止一种。写 TypeScript 时我最常用 value instanceof Array,看过不少文章说这个方法可靠,因为它会递归比较原型链,看到 Array 就返回 true。在 Node 里它够用,但在浏览器里有几种情况会被它判错。

下面把常见方法过一遍,顺带说说各自的边界条件。

流水的脚本,铁打的 C++

最稳的办法是 V8 内部的 Value->IsArray()

  • Node 和浏览器把它包装成 Array.isArray(value),从引擎内部判断变量的真实类型。
  • 通用性、速度、稳定性都最好。
  • 唯一的限制是不支持 ES5 的老浏览器没有这个 API。

盲人摸象

剩下几种在大多数场景下能工作,但都有破绽。

value instanceof Array;

instanceofvalue.constructor === Array 机理接近——都靠”同一个 Array 引用”来判断。两个 gotcha:

  • 某些副作用库可能把 Array 替换成另一个类,方法就失效。
  • 浏览器里 service worker 或 iframe 是各自独立的 realm,每个 realm 有自己的 Array,跨 realm 的数组对外层 realm 的 Array 来说就不再 instanceof Array
Object.prototype.toString.call(value) === "[object Array]";

绝大多数情况能工作。例外是”类数组”——可迭代但不是真数组的对象(argumentsNodeList、自定义实现 Symbol.iterator 的对象等等),对这些它返回 "[object Object]"

实战选择

简单说:

  • 能用 Array.isArray 就用它——它对跨 realm 的数组也会返回 true,而 instanceof 不会。
  • 只有兼容到 ES5 之前的老浏览器才需要回退到 Object.prototype.toString.call,并对类数组单独走 Symbol.iterator 判断。
  • instanceof Array 只在能确定不会跨 realm、Array 也不会被改写的小项目里才合适。
返回首页