全部 / 前端 / 技术 · 2022年8月29日 0

检测一个值为数组

卡通插画喊你回来上班公众号封面首图__2022-08-29+12_45_21

检测一个值是否为数组有几种方法?今天我们就来聊聊,在 JavaScript 中检测数据类型的一般有 typeof 和 instanceof 两个运算符。但是 typeof 在检测数组时:

typeof []
typeof Array(3)
typeof new Array(3)

返回的全是:"object",所以使用 typeof 是不能检测的。当然 instanceof 是可以检测的,但它是有局限性的后面会说明,那除了 instanceof 还有别的方法吗?这次我总结了4种方法:

Array.isArray(value)
value instanceof Array ( Object.getPrototypeOf(value) == Array.prototype )
value.constructor == Array
Object.prototype.toString.call(value) == "[object Array]"

下面我们依次来说下:

Array.isArray:

语法:

Array.isArray(value)

实例:

// all following calls return true
Array.isArray([]);
Array.isArray([1]);
Array.isArray(new Array());
Array.isArray(new Array('a', 'b', 'c', 'd'));
Array.isArray(new Array(3));
// Little known fact: Array.prototype itself is an array:
Array.isArray(Array.prototype); 

// all following calls return false
Array.isArray();
Array.isArray({});
Array.isArray(null);
Array.isArray(undefined);
Array.isArray(17);
Array.isArray('Array');
Array.isArray(true);
Array.isArray(false);
Array.isArray(new Uint8Array(32));
Array.isArray({ __proto__: Array.prototype });

兼容性:

这也是现在推荐的检测值为数组的方法。

instanceof:

语法:

object instanceof constructor

实例:

const array = [1, 2, 3];
const object = { message: 'Hello!' };
const string = 'Hello!';
const empty = null;

array  instanceof Array; // => true object instanceof Array; // => false
string instanceof Array; // => false
empty  instanceof Array; // => false

兼容性:

instanceof 的原理其实就是寻找构造函数的原型是否出现在被检测值的原型链上,可以使用 Object.getPrototypeOf 获取原型:

function C() {}
function D() {}

let o = new C()

o instanceof C // true
Object.getPrototypeOf(o) == C.prototype // true

但 instanceof 有个弊端,在多上下文也就是存在 frames 或 windows 时:

let iframeEl = document.createElement('iframe')
document.body.appendChild(iframeEl)
iframeArray = window.frames[window.frames.length - 1].Array

let arr1 = new iframeArray(1,2,3,4)
let arr2 = new Array(1,2,3,4)

arr1 instanceof Array   // false
arr2 instanceof Array  // true

究其原因是:

Array.prototype != iframeArray.prototype

constructor:

语法:

value.constructor

实例:

value.constructor == Array

同样和 instanceof 有类似的问题:

let iframeEl = document.createElement('iframe')
document.body.appendChild(iframeEl)
iframeArray = window.frames[window.frames.length - 1].Array

let arr1 = new iframeArray(1,2,3,4)
let arr2 = new Array(1,2,3,4)

arr1.constructor == Array // false

Object.prototype.toString

语法:

Object.prototype.toString.call(value)

实例:

Object.prototype.toString.call(value) == "[object Array]"

此方法还是比较老牌的没有多上下文问题,在 Array.isArray 没有的情况下建议选择该方法:

if (!Array.isArray) {
  Array.isArray = function(arg) {
    return Object.prototype.toString.call(arg) === '[object Array]';
  };
}

另外多说一下其它库是如何检测的:

underscore.js 经历了如下几种方法:

  1. 特征检测,带有 concat 和 unshift 方法的为数组类型
var nativeIsArray = Array.isArray,

_.isArray = nativeIsArray || function(obj) {
  return !!(obj && obj.concat && obj.unshift);
};
  1. toString
var nativeIsArray = Array.isArray

function tagTester(name) {
  var tag = '[object ' + name + ']';
  return function(obj) {
    return toString.call(obj) === tag;
  };
}
var isArray = nativeIsArray || tagTester('Array');

jquery

jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
  class2type[ "[object " + name + "]" ] = name.toLowerCase();
});
 
$.type = function( obj ) {
  ...
  return typeof obj === "object" || typeof obj === "function" ?
    class2type[ core_toString.call(obj) ] || "object" :
    typeof obj;
}

// 判断数组
isArray: Array.isArray || function( obj ) {
  return jQuery.type(obj) === "array";
},