JS中数据类型检测的四种方案
-
typeof检测数据类型的运算符 -
instanceof检测该实例是否为该类的实例 -
constructor用于获取实例或类的构造函数 -
Object.prototype.toString.call()用于Object类原型上检测数据类型的方法
typeof
typeof操作符返回一个字符串,表示未经计算的操作数的类型
let num = 10;
console.log(typeof num); // number
let str = 'lucky';
console.log(typeof str); // string
let bool = true;
console.log(typeof bool); // boolean
let und = undefined;
console.log(typeof und); // undefined
let sym = Symbol('sym');
console.log(typeof sym); // symbol
let obj = {name:'lucky'};
console.log(typeof obj); // object
let func = function(){};
console.log(typeof func); // function
typeof typeof num; // => 这里不论 num 是什么类型,最终的结果都是: "string"
typeof 原理
typeof操作符是按照数据在计算机底层存储的二进制结果来进行检测的
null:typeof null的结果是"Object"是由于null在底层中存储的二进制是:000000,对象在底层存储的二进制是000xxx;所以使用typeof检测null的时候会打印出"Object"。在之后ECMAScript也曾经提出过修正案,但是由于导致浏览器中出现typeof null === 'null',可能会导致旧项目不兼容的情况,所以最后提案被拒绝了。对象类型:所有的对象类型的数据在浏览器底层的存储的二进制都是000开头,所以基于typeof检测对象类型的结果都是"Object",这就导致typeof无法细分对象和数组。
instanceof
为了解决
typeof无法区分具体对象的缺点,于是就把instanceof这个操作符拉过来用做数据类型检测具体对象数据类型
instanceof不是用于检测数据类型的,而是用来检测当前实例是否属于这个类instanceof一般只能用于普通对象、数组对象、正则对象、日期对象等对象的具体细分instanceof不是专业的数据类型检测,所以存在很多的问题
结果为
true也不一定是Object的实例
let arr = [];
arr instanceof Array; // true
arr instanceof Object; // true
arr instanceof RegExp; // false
instanceof无法用于检测基本数据类型
let n = 10;
let m = new Number(10);
//能够调用Number原型上的方法,那么它一定是Number类的实例
console.log(n.toFix(2)); //"10.00" => 基本类型值在调用原型上的方法时,浏览器会将基本类型值Object(n)处理一下,然后他就可以基于原型链查找方法了
console.log(m.toFix(2)); //"10.00"
n instanceof Number; //false
m instanceof Number; //true
手动修改原型链,会影响
instanceof的检测结果
function Person(){}
Person.prototype = Array.prototype;
let p1 = new Person;
p1 instanceof Array; // true
instanceof原理
基于" 实例 instanceof 类" 检查数据类型的时候,浏览器底层会将它处理成 “类.hasInstance(实例)” 的方式。(所有类都是Function的实例)
let arr = [];
arr instanceof Array; //true
Array[Symbol.hasInstance](arr); //true
let obj = {};
arr instanceof obj; //对象不是function的实例,他没有这个属性
Symbol.hasInstance方法执行的原理
- 判断当前实例的原型链上
__proto__是否存在这个类的原型prototype arr.__proto__ === Array.prototype满足条件arr instanceof Array返回truearr.__proto__.__proto__ === Object.prototype同样满足条件arr instanceof Object返回true
重写instanceof
/*
* [parmas1] obj 要检测的实例
* [patmas2] constructor 要检查的类
*/
function instance_of(obj,constructor){
// 参数校验
if(obj == null || !/^(Object|Function)$/i.test(typeof obj)) return false;
if(typeof constructor !== 'function') throw new TypeError("Right-hand side of 'instanceof' is not callable");
// obj.__proto__ === Object.getPrototypeOf(obj) 这里使用兼容写法
let proto = Object.getPropertyOf(obj),
prototype = constructor.prototype;
while(true){
if( proto === null ) return false;
if( proto === prototype ) return true;
proto = Object.getPropertyOf(proto);
}
}
constructor
constructor是原型上一个指向构造函数的属性,它同样不是用于专业的数据类型检测的
let arr = [];
console.log(arr.constructor === Array); //true 在constructor不被修改的情况下,这样区分是数组还是普通对象
console.log(arr.constructor === Object); //false
console.log(arr.constructor === RegExp); //false
原型重定向
function Person() {}
Person.prototype = Array.prototype;
let p1 = new Person;
console.log(p1.constructor === Array); //true 一但原型重定向,constructor也改了,所以也就不准了
检查基本数据类型,相较于instanceof来说对基本数据类型检查的支持更好
let n = 10;
let m = new Number(10);
console.log(n.constructor === Number); //true
console.log(m.constructor === Number); //true
Object.prototype.toString.call()
专业的用于检测数据类型的方法,除了代码长,可以说是零瑕疵
- 许多类原型上的
toString方法都是用于转换字符串的,而Object.prototype.toString是用于检测数据类型的,所以使用时一般搭配call来改变this的指向 - ``Object.prototype.toString.call()
的返回结果是[object Xxxxxx],Xxxxxx的可能是对象[Symbol.toStringTag]、对象.constructor或者Object`。
let class2type = {},
toString = class2type.toString; //Object.prototype.toString
toString.call(10); //[object Number]
toString.call('A'); //[object String]
toString.call(true); //[object Boolean]
toString.call(null); //[object Null]
toString.call(undefined); //[object Undefined]
toString.call(Symbol()); //[object Symbol]
toString.call(/^$/); //[object RegExp]
toString.call(function()); //[object Function]
toString.call(function* ()); //[object GeneratorFunction]
将
constructor修改也不会影响最后的返回值
// 所以可以借用Symbol.toStringTag来修改自定义类的检测结果
class Person {
// 只要获取实例的[Symbol.toStringTag]属性值,则调用这个方法
get[Symbol.toStringTag]() {
return "Person";
}
}
let p1 = new Person;
toString.call(p1) => "[object Person]"
jQuery中的数据类型检测方法
jQuery中封装的数据类型检测的方式或许不是最好的,但是也是提供了一个很好的思路
var class2type = {};
var toString = class2type.toString;
["Boolean","Number","String","Function","Array","Date","RegExp","Object","Error","Symbol"].forEach((type)=>{
class2type[`object ${type}`] = type.toLowerCase();
})
function toType(obj){
if(obj == null){
return obj + '';
}
return typeof obj === "object" || typeof obj === "function" ?
class2type[toString.call(obj)] | "object" :
typeof obj;
}