【JS深入】检测对象是否为数组

这是我在面试的时候碰到的一个问题,但是只是简单回答用Array.isArray()判断,当时也被问了有没有其他方法,这个方法有什么问题,一时语塞,学习不够。回来之后也是查找了一些资料,找到了一些方法。自己也算是总结了一下,并分析其优劣。

1、typeof

typeof算是我们平时检测数据类型用到的最多的方法,但它的局限也只是适用基本数据类型的检测,对于Array对象,typeof 的结果只是 object,无法更进一步判断。

2、constructor

这里主要用到了原型对象的 constructor 属性,实例本身没有constructor属性,但是如果一个对象是另一个对象的实例, 那么这个实例就会继承原型的构造函数属性。

var test = [1,2,3,4,5,6];

test.constructor === Array;
// true

test.constructor === [].constructor;
// true

3、prototype

一个构造函数实例化之后,实例的内部属性[[prototype]]会指向它的原型对象。利用这一点,我们也可以检测某个对象是否是数组。

var a = [1,54,7,8,9];
a.__proto__ === Array.prototype;
//true

4、instanceof

这个方法用来检测某一个对象是否是另一个对象的实例,如果一个对象是数组的话,那么它应该也是Array的一个实例。

var a = [1,2,4,6,7];
a instanceof Array;
//true

5、Object.prototype.toString()

上面的方法貌似没什么问题,但是它们都是基于原型链来判断的,如果存在两种执行环境呢?比如两个iframe,各自都有自己的一套执行环境,它们的实例之间是不共享原型链的。

var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
xArray = window.frames[window.frames.length-1].Array;
var a = new xArray(1,2,3);

a instanceof Array;
//false 
a instanceof xArray;
// true

a.constructor === Array;
//false
a.constructor === xArray;
//true

a.__proto__ === Array.prototype
//false
a.__proto__ === xArray.prototype
//true

因为执行环境的不一致,直接判断一个对象是否是Array的实例,就会出现问题。这时,我们就需要用 Object.prototype.toString() 来做判断了。这个方法应该算是目前来说最可靠的方法了。

Object.prototype.toString.call(a) === "[object Array]";
//true

 对JS对象应用Object.prototype.toString的过程大概是:首先,取得对象的一个内部属性 [[Class]],然后依据这个属性,返回一个类似于 "[object Array]" 的字符串作为结果(看过ECMA标准的应该都知道,[[]]用来表示语言内部用到的、外部不可直接访问的属性,称为“内部属性”)。利用这个方法,再配合call,我们可以取得任何对象的内部属性[[Class]],然后把类型检测转化为字符串比较,以达到我们的目的。

6、Array.isArray()

这时ES5新增的一个专门用于Array对象检测的方法。不过支持IE9以上,IE8一下的浏览器是不兼容的。

var a = [1,34,56,7,8,34];
Array.isArray(a);
// true

总结

在不考虑IE8及以下的兼容时,直接用Array.isArray是最方便的;考虑兼容性的话,直接用Object.prototype.toString()是最可靠的。