判断数组的方法

236 阅读3分钟

通过instanceof来判断

判断原理

instance是通过判断构造函数的prototype属性是否出现在对象的原型链的任何位置,返回一个布尔值

let a = [];
a instanceof Array; //true
let b = {};
b instanceof Array; //false

在以上代码中,instance检测Array的prototype属性是否存在在a的原型链上,显然a是一个数组,拥有Array.prototype属性,所以为true。

可能存在的问题

  1. prototype属性是可以进行修改的,所以并不是最初判断为true就一定永远为真
  2. 当我们的环境拥有多个全局环境的时候,例如html中拥有多个iframe对象,instanceof的验证结果可能不会符合预期,例如:
//为body创建并添加一个iframe对象
var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
//取得iframe对象的构造数组方法
xArray = window.frames[0].Array;
//通过构造函数获取一个实例
var arr = new xArray(1,2,3); 
arr instanceof Array;//false

导致这个的原因是iframe会产生新的全局环境,它会拥有自己的Array.prototype属性,让不同环境下的属性相同很明显是不安全的做法,所以Array.prototype !== window.frames[0].Array.prototype,想要arr instanceof Array为true,你得保证arr是由原始Array构造函数创建时才可行。

通过constructor判断

我们知道,实例的构造函数属性constructor指向构造函数,那么通过constructor属性也可以判断是否为一个数组。

let a = [1,3,4];
a.constructor === Array;//true

同样,这种判断也会存在多个全局环境的问题,导致的问题与instanceof相同。

//为body创建并添加一个iframe标签
var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
//取得iframe对象的构造数组方法
xArray = window.frames[window.frames.length-1].Array;
//通过构造函数获取一个实例
var arr = new xArray(1,2,3); 
arr.constructor === Array;//false

通过Object.prototype.toString.call()来判断

方法

它的强大之处在于,不仅可以检测数组,还可以检测对象,函数。

//检验是否是函数
let a = function () {};
Object.prototype.toString.call(a) === '[object Function]';//true
//检验是否是数字
let b = 1;
Object.prototype.toString.call(a) === '[object Number]';//true

原理

因为toString这个方法返回的是反应类型的字符串

但是为什么不用obj.toString()来进行判断呢?

console.log("jerry".toString());//jerry
console.log((1).toString());//1
console.log([1,2].toString());//1,2
console.log(new Date().toString());//Wed Dec 21 2016 20:35:48 GMT+0800 (中国标准时间)
console.log(function(){}.toString());//function (){}
console.log(null.toString());//error
console.log(undefined.toString());//error

通过obj.toString()方法和Object.prototype.toString.call()方法结果却不相同,这是因为Array和Function作为Object的实例,将toString方法进行了重写,然后根据原型链的知识,调用的就是Array和Function上的toString方法,所以结果不一样

通过Array.isArray()来判断

Array.isArray() 用于确定传递的值是否是一个数组,返回一个布尔值。

let a = [1,2,3]
Array.isArray(a);//true

简单好用,而且对于多全局环境,Array.isArray() 同样能准确判断,但有个问题,Array.isArray() 是在ES5中提出,也就是说在ES5之前可能会存在不支持此方法的情况。怎么解决呢?

最终解决方案

当然还是用Array.isArray(),从ES5新增isArray()方法正是为了提供一个稳定可用的数组判断方法,不可能专门为此提出的好东西不用,而对于ES5之前不支持此方法的问题,我们其实可以做好兼容进行自行封装,像这样:

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