简介
我们在代码中经常要对数据类型进行判断,大家熟知的应该是 typeof,那么它有什么不足,有没有其他方法来进行类型检测呢?
1. typeof
JS 的值包括基本类型和引用类型。基本类型值有以下六种:Number、String、Boolean、Undefined、Null 和 Symbol 。引用类型值即保存在内存中的对象 Object。
typeof 是一个操作符,其右侧跟一个一元表达式,并返回这个表达式的数据类型。返回的结果用该类型的字符串(全小写字母)形式表示,包括以下 7 种:number、string、boolean、undefined、symbol、object、function 等。
可以看到七种值类型和 tyoeof 能够分辨出的七种数据类型并不是以一一对应的。
// 返回对应的基本类型
console.log(typeof 1); // "number" (对应 Number)
console.log(typeof '1'); // "string" (对应 String)
console.log(typeof true); // "boolean" (对应 Boolean)
console.log(typeof undefined); // "undefined" (对应 Undefined)
console.log(typeof Symbol(1)); // "symbol" (对应 Symbol)
console.log(typeof {}); // "object" (对应 Object)
// 没有基本类型与之对应
console.log(typeof new Function()); // "function"
// 返回的不是对应的基本类型
console.log(typeof null); // "object"
// 无法检测细分对象类型
console.log(typeof [] ); // "object"
console.log(typeof new Date()); // "object
console.log(typeof new RegExp()); // "object
// 可以检测细分对象类型
console.log([] instanceof Array); // true
console.log({} instanceof Object); // true
console.log(new Date() instanceof Date); // true
function Person(){}
console.log(new Person() instanceof Person); // true
局限性:
- null 的检测结果不符合我们的认知,因为其并没有返回对应基本类型值的 "null" 而是 “object”
- 不能区分数组,日期,正则或者其他的具体类型,因为返回的都是原型链顶端的 ”object”
2. instanceof
instanceof 是用来检测一个实例是否属于某个类。
// 可以检测原型链上所有类
console.log([] instanceof Object); // true
console.log(new Date() instanceof Object);// true
function Person(){}
console.log(new Person() instanceof Person); // true
// 基本类型值(number, string, boolean)转成包装类型才能进行检测
console.log(1 instanceof Number); // false
console.log(new Number(1) instanceof Number); // true
console.log('1' instanceof String); // false
console.log(new String('1') instanceof String); // true
console.log(true instanceof Boolean); // false
console.log(new Boolean(true) instanceof Boolean); // true
// 没有包装类型的基本类型值(undefined, null, symbol)无法检测
console.log(undefined instanceof Undefined); // Uncaught ReferenceError: Undefined is not defined
console.log(undefined instanceof Object); // Uncaught ReferenceError: Null is not defined
console.log(null instanceof Null); // false
console.log(null instanceof Object); // false
console.log(Symbol('1') instanceof Symbol); // false
console.log(Symbol('1') instanceof Object); // false
局限性:
- 只要在当前实例的原型链上,我们用其检测出来的结果都是true
- 基本类型值检测繁琐,且检测不全(undefined, null, synbol)
- 原型链是可以手动修改的
3. constructor 构造函数
构造函数原型上有一个 constructor 属性指向构造函数自身的,参见JS入门难点解析12-继承的实现方式与优缺点。那么如果在实例上使用 construtor 时,就会直接使用其构造函数原型的上的该属性,并指向其构造函数。
// 可以检测其构造函数
console.log([].constructor === Array); // true
console.log({}.constructor === Object);// true
function Person(){}
console.log(new Person().constructor === Person);// true
// 原型链不会干扰
console.log([].constructor === Object); // false
// 可以检测 Symbol, 因为 Symbol 本身是一个函数(但是不支持 new)
console.log(Symbol('1').constructor === Symbol); // true
局限性:
- 基本类型值检测繁琐,且检测不全(undefined, null)
- constructor 是可以手动修改的
4. Object.prototype.toString
toString() 是 Object 的原型方法,调用该方法,默认返回当前对象的 [[Class]] 。这是一个内部属性,其格式为 [object Xxx] ,其中 Xxx 就是对象的类型。
对于 Object 对象,直接调用 toString() 就能返回 [object Object] 。而对于其他对象,则需要通过 call / apply 来调用才能返回正确的类型信息。
这里需要注意,其实内置对象比如 Number, Array 等对象上重写了 toString,用来转换为字符串。所以我们使用 Object.prototype.toString 来进行判断。
// 可以检测其构造函数
console.log(Object.prototype.toString.call([])); // [object Array]
console.log(Object.prototype.toString.call({})); // [object Object]
function Person(){}
console.log(Object.prototype.toString.apply(new Person())); // [object Person]
// 可以检测所有基本类型
console.log(Object.prototype.toString.apply(undefined)); // [object Undefined]
console.log(Object.prototype.toString.apply(null)); // [object Null]
console.log(Object.prototype.toString.apply(Symbol(1))); // [object Symbol]
缺陷:
基本没缺陷,要说有,就是检测 number 等这类有包装类型的,需要检测其包装类型。
小结
综上可知,如果场景简单,要求不严格,随便怎么用都 okay。但是,如果要严格保证获取的准确性和粒度,建议用 Object.prototype.toString。
以上所述都是获取一个值所属的类型,如果要看反过来看一个类型是否是某值的原型,可以使用
isPrototypeOf -- 判断的是原型
Symbol.hasInstance - 可以改写该属性做一些特殊的判断
等来判断
参考
判断JS数据类型的四种方法
JavaScript数据类型检测的四种方式
JS中数据类型的判断( typeof,instanceof,constructor,Object.prototype.toString.call() )