JS 里怎么判断一个值是不是数组
Array.isArray、instanceof、Object.prototype.toString.call 的边界与选择。
发布 2018年9月09日 标签
#javascript
#types
~/posts/is-array $ cat post.md
概览
判断一个值是不是数组的方法不止一种。写 TypeScript 时我最常用 value instanceof Array,看过不少文章说这个方法可靠,因为它会递归比较原型链,看到 Array 就返回 true。在 Node 里它够用,但在浏览器里有几种情况会被它判错。
下面把常见方法过一遍,顺带说说各自的边界条件。
流水的脚本,铁打的 C++
最稳的办法是 V8 内部的 Value->IsArray():
- Node 和浏览器把它包装成
Array.isArray(value),从引擎内部判断变量的真实类型。 - 通用性、速度、稳定性都最好。
- 唯一的限制是不支持 ES5 的老浏览器没有这个 API。
盲人摸象
剩下几种在大多数场景下能工作,但都有破绽。
value instanceof Array;
instanceof 和 value.constructor === Array 机理接近——都靠”同一个 Array 引用”来判断。两个 gotcha:
- 某些副作用库可能把
Array替换成另一个类,方法就失效。 - 浏览器里 service worker 或 iframe 是各自独立的 realm,每个 realm 有自己的
Array,跨 realm 的数组对外层 realm 的Array来说就不再instanceof Array。
Object.prototype.toString.call(value) === "[object Array]";
绝大多数情况能工作。例外是”类数组”——可迭代但不是真数组的对象(arguments、NodeList、自定义实现 Symbol.iterator 的对象等等),对这些它返回 "[object Object]"。
实战选择
简单说:
- 能用
Array.isArray就用它——它对跨 realm 的数组也会返回true,而instanceof不会。 - 只有兼容到 ES5 之前的老浏览器才需要回退到
Object.prototype.toString.call,并对类数组单独走Symbol.iterator判断。 instanceof Array只在能确定不会跨 realm、Array也不会被改写的小项目里才合适。