几种判断数组的方法

一、前言

众所周知,js是门“动态”、“弱类型”编程语言,这意味着在js中可以任性定义变量,同时,“任性”也意味着需常在项目开发中对变量做类型判断,曾几何时,对数组变量的类型判断是件很痛苦的事情,开发人员想出多种方案来对数组做出准确的类型判断,但效果不佳,直到ES5标准“入主中原”,判断数组类型有了标准的isArray()官方利剑,才降伏了数组类型判断这条恶龙,世间得一清,但在此之前开发者是如何判断数组类型的?判断数组类型为何会如此玄学?为何要判断数组类型?带着这些疑问,吾跋山涉水,探寻各方资料,整理消化后遂成此文,以记之。

二、判断js数组类型为何麻烦?

1、语言本身的“缺陷”

js是门“动态”“弱类型”编程语言,这意味着js在定义和使用变量时可以“任性”,在ES6之前,我们定义变量一般使用“var”来定义:

1
2
3
var name = 'jack';
name = 20;
name = ['aa'];

在上述例子中,name变量初始定义为字符串类型,而后变为数字类型,最后摇身一变成为数组类型,这种任性摇摆的特性就是其“动态”特性,在java中我们定义一个字符串变量须如此定义:String name = ‘jack’,java通过一个String前缀“显式的”、“强制的”指定name变量为字符串类型,之后不得对该name变量进行类型变换(如果执行name = 22将会报type类型转换错误),但js采用的是弱类型定义方案,在定义变量时使用var声明了一个变量,弱化了类型前缀的限制,并没强制锁死变量类型,之后可以随意更改其类型。动态弱类型这种声明变量的方案用起来可以随性而为,无须顾虑太多,随性的代码书写如若不加管制必将招致灾难性的代码bug。

2、js类型判断的“不足”

其实动态弱类型的语言特性并不是决定js判断数组类型麻烦的必然原因,js语言因为历史原因,其创造者在开发之初将其定位为简单的网页小助手语言,为了轻巧、快速的完成小任务开发选择了“动态弱类型”的语言方案,PHP亦为动态弱类型语言,但在处理类型判断时,PHP用一个gettype()方法可以轻松、精准的搞定(PHP作为世界上世界上最好的语言还是有点东西的🤣),PHP有gettype()这枚银弹,js有吗,嗯,算有吧,js最常用的是用typeof操作符来获取数据类型,看typeof这个名字是不是感觉很厉害?感觉会跟PHP一样轻松简单?但随后你会发现:typeof操作符是个很局限的类型获取方案,用它对基本数据类型做判断还算过得去,但在涉及到引用类型判断这种细活时就显得很low了…

三、判断js数组类型的几个“方案”

1.typeof

typeof在判断基础数据类型时尚有问题,更别说用来判断子孙繁多的引用类型了,typeof在判断引用类型时一刀切的统统返回object, 如

1
2
3
4
5
6
7
var obj = {};
var arr = [];
var map = new Map();

typeof obj; // object
typeof arr; // object
typeof map; // object

故此,不适应于判断数据,PASS

2.data instanceof Array

instanceof是js用来判断继承关系的运算符(js基于原型链实现继承,故instanceof判断的就是对应的类是否存在于变量的原型链上),根据这个特性可以如此来判断数组类型:

1
2
const arr = [1, 2, 3];
console.log(arr instanceof Array); // true

控制台打印显示如下:

效果图显示失败

从打印的结果可以看到Array存在于数组[1, 2, 3]的原型链上,故[1, 2, 3] instanceof Array === true; 利用instanceof的这个特性可以判断数组类型,但是instanceof运算符有个弊端 就是arr instanceof Object也是返回true:

1
2
const arr2 = [1, 2, 3];
console.log(arr2 instanceof Object); // true

控制台打印显示如下:

效果图显示失败

故此,不适应于判断数据,PASS

3.constructor

JavaScript中, constructor属性会返回对象的构造函数。

1
2
3
4
let arr = [1, 2, 3];
console.log(arr.constructor === Array); // true
let arr = [1, 2, 3];
console.log(arr.constructor === Object); // false

控制台打印显示如下:

效果图显示失败

从打印结果显示,是满足我们的数据类型判断的,并且没有出现第二种判断方式instanceof方法的问题,因此是可以采用的

4.Object.prototype.toString()

Object.prototype.toString()能获取到变量的“类目名”,在js中万物皆为对象,万物皆有“类目名”,每个变量、对象、数组等都有一个唯一的类目名(这个类目名类似于人类给各类动植物起的“学名”),该方案通过获取目标变量的类目名([object Array])进行判断,如果类目名一致则证明目标变量为数组类型:

1
2
let arr = [1, 2, 3];
console.log( Object.prototype.toString.call(arr) === '[object Array]' ); // [object Array]: string; 返回 true

Tips: 这种方法获取各种类型数据都有与之对应的字符串匹配值

1
2
3
4
5
6
7
8
Object.prototype.toString.call([]); // "[object Array]"
Object.prototype.toString.call({}); // "[object Object]"
Object.prototype.toString.call(''); // "[object String]"
Object.prototype.toString.call(1); // "[object Number]"
Object.prototype.toString.call(function f(){}); // "[object Function]"
Object.prototype.toString.call(); // "[object Undefined]"
Object.prototype.toString.call(null); // "[object Null]"
Object.prototype.toString.call(document); // "[object HTMLDocument]"

从打印结果显示,是满足我们的数据类型判断的,因此是可以采用的

5.Array.isArray()

该方法是ES5标准规定的判断数组类型的标准方法,虽然Object.prototype.toString()方法可用来判断数组类型,但未免显得有点hack,又因自家typeof类型操作符给予厚望,辱没众望,如果随便更改typeof的返回结果势必会导致天下大乱,instanceof运算符又存在不同frame的局限性难堪大任,ES5不得不亡羊补牢的设计了isArray()方法来“增量”的解决数组判断难题。

1
2
Array.isArray([1, 2, 3]); // true
Array.isArray({}); // false

从打印结果显示,是满足我们的数据类型判断的,因此是可以采用的

四、为何要判断数组类型?

前面说过js属于动态弱类型语言,可能某个变量用着用着就莫名其妙的变了类型(自己不小心更改类型,引入的第三方代码库,因为同名变量改变了类型),如果你设想的是某个变量为数组类型,但因某个逻辑变成了基本类型,这时如果调用数组的方法注定会报错,凡此种种导致的问题,数不胜数,具体的问题实践多了懂得就懂。

五、结语

近几年前端项目愈发复杂庞大,为更好的构建高性能的前端项目,诞生了“react、angular、vue”等数据驱动型解决方案,大量的数据、大量的组件和类对数据类型的判断需求愈发频繁,但因为js动态弱类型语言特性,加之其类型判断的坑爹性,所以各路开发者希望完善和升级js,在ES6标准中,新的const变量定义方案能很好的应对变量动态性问题,微软开发的“typescript”能够实现强类型变量定义,可应对弱类型定义问题。这些方案极大的减少了早期js变量任性定义带来的各种问题,虽然判断数组类型在未来开发中可能会成为历史云烟,但理解其相关的基础和历史演变却是一件很【浪漫】的事情,因为在理解了它的相关坑爹性和进化史有助于我们更好的思考和优化。爱之深,责之切,希望js能在未来变得更加锋利可靠,也希望少为一些坑爹特性而想出一些hack方案(额,比如——>Object.prototype.toString()方法)。