前端开发系列037-基础篇之类型检测
JavaScript 存在很多中数据类型,按照一贯的区分方式,我们把这些数据类型分为两大类,分别是原始(基本)数据类型和对象类型。
其中基本( 原始 )数据类型有6种,分别是 null undefined boolean string number symbol
,对象类型则为object
,对象类型中包含Object、Array、RegExp、Function、Error、Set、Map、WeakMap
等。
在JavaScript 代码中我们经常需要对数据的类型进行检查,检查数据类型的方式主要有四种:
- typeof 关键字
- instanceof 关键字
- Object.prototype.toString.call()
- constructor 构造器属性
typeof
关键字是检查数据类型最简单也最常用的方法。
console.log(typeof "abc") /* string */
console.log(typeof 12345) /* number */
console.log(typeof true) /* boolean */
console.log(typeof undefined) /* undefined */
console.log(typeof Symbol()) /* symbol */
console.log(typeof null) /* object */
console.log(typeof typeof typeof Symbol()) /* string */
console.log(typeof {}) /* object */
console.log(typeof []) /* object */
console.log(typeof /abc/) /* object */
console.log(typeof function(){}) /* function */
我们在使用typeof
关键字的时候,有一些注意点。
首先就是 typeof null
得到的结果为object
而非null
,这在 JavaScript 语言中被认为(承认)是一个设计错误,typeof null
被判定为 object
的原因在于 null
本身表示什么都没有,所以在计算机中表示为一串0,而计算机内部处理的时候如果读取的数据总是以000
开头,那么就将被认为是对象类型的数据。
其次,typeof
关键字在对象类型进行运算的时候得到的都是object
,只有函数类型比较特殊得到的是function
,也就是说我们无法通过该关键字来检查给定的数据是否是数组、或者是正则表达式等等。如果需要获取这些结构的数据类型,或者说是获取这些数据的构造函数,常用的方式是借用 Object 原型对象上面的 toString 方法来实现,该方法总是会返回一个[object 构造函数]
结构的字符串。
JavaScript 语言中的Object.prototype.toString
方法总是会返回[object 构造函数]
结构的字符串,其中方括号中前面的object
表明该数据是对象类型的,后面的则是创建该实例的构造函数。
如果是一个普通的对象,譬如{name:"zs"}
,在调用该方法的时候 [代码为:({name:"zs"}).toString()
]得到的结果为[object object]
, 但因为原型链方法覆盖的问题,数组或其它类型的数据则无法直接调用该方法。对于除普通对象外的其它对象类型的数据而言,它们需要通过 call 或 apply 绑定 this 的方式才能调用该方法。
console.log(({}).toString()) /* [object Object] */
console.log(Object.prototype.toString.call({})) /* [object Object] */
console.log(Object.prototype.toString.call([])) /* [object Array] */
console.log(Object.prototype.toString.call(new Function)) /* [object Function]*/
console.log(Object.prototype.toString.call(new RegExp)) /* [object RegExp] */
console.log(Object.prototype.toString.call(new Error)) /* [object Error] */
console.log(Object.prototype.toString.call(new Set)) /* [object Set] */
console.log(Object.prototype.toString.call(new Map)) /* [object Map] */
一般而言Object.prototype.toString
方法很好用,But 它也不是万能的,譬如它无法处理自定义类型的类型检查( 包括自定义构造函数 和 Class 等)。
function Person() {};
let p = new Person;
class Animal {};
let a = new Animal;
console.log(Object.prototype.toString.call(p)) /* [object Object] */
console.log(Object.prototype.toString.call(a)) /* [object Object] */
对于自定义类型的数据,我们需要通过 instanceof 或者是 constructor 来进行检查。
JavaScript 语言中的 instanceof
关键字用于检查某个对象是否是指定构造函数创建的实例对象,在检查的时候会检查整条原型链。如果准确点说,那么instanceof
实际上检查的是某个对象( 左值 )是否在指定构造函数( 右值 )的原型链上面,如果在那么就返回 true ,否则的话就返回 false。
console.log(p instanceof Person) /* true */
console.log(a instanceof Person) /* false */
console.log(a instanceof Animal) /* true */
console.log(a instanceof Function, a instanceof Object) /* false true */
console.log(a.constructor == Person); /* false */
console.log(a.constructor == Animal); /* true */
console.log(a.constructor == Object); /* false */
console.log(p.constructor == Person); /* true */
console.log(({}).constructor == Object) /* true */
console.log(([]).constructor == Array) /* true */
这里试着给出instanceof
关键字的实现代码,其实就是个死循环和循环内原型查找而已。
/* instanceof 实现原理 */
function mockInstanceof(instance,constructor){
let B = constructor.prototype;
let A = instance.__proto__;
while(true)
{
/* Object.prototype.__proto__ -> null */
if (A == null){
return false;
}
if (A === B){
return true;
}
A = A.__proto__;
}
}
/* 测试数据 */
function Person() { };
function Boy() { };
let p = new Person;
let b = new Boy;
console.log(mockInstanceof(p, Person)) /* true */
console.log(mockInstanceof(p, Object)) /* true */
console.log(mockInstanceof(b, Boy)) /* true */
console.log(mockInstanceof(b, Person)) /* false */