前言
首先,我们知道JavaScript是一种弱数据语言,它在声明的时候并不需要确定变量的类型,而是在js运行过程中会自动判断数据的类型。
那么我们该如何去获取js变量的数据类型呢?
js为我们提供了四种常用的方式:分别是typeof检测、instanceof检测、constructor检测、和Object.toString.call() 检测。
我们先定义一些常用的数据类型:
// 数字类型
let num = 111
// 字符串类型
let str = 'Hello'
// 布尔类型
let boolean = false
// 数组类型
let arr = []
// 对象类型
let obj = {}
// 函数类型
let testFunction = function () {}
// 类
let testClass = class {}
第一种:typeof 检测数据类型
typeof可以判断简单数据类型的判断。返回值有number、string、boolean、undefined、object和function。 typeof检测数据类型的返回值是对应数据类型的小写字符串形式。例如变量num是Number类型,则typeof num的返回值就是number。
console.log(typeof num); //number
console.log(typeof str); //string
console.log(typeof boolean); //boolean
console.log(typeof arr); //object
console.log(typeof Array); //function
console.log(typeof obj); // object
console.log(typeof Object); // function
console.log(typeof testFunction); //function
console.log(typeof testClass); //function
console.log(typeof undefined); //undefined
console.log(typeof null); //object
console.log(typeof xxx); // undefined
通过上述代码我们可以发现,typeof操作符在进行数据类型判断的时候,可以准确的判断简单数据类型。 至于typeof null的返回值为什么会是object,这属于JavaScript的历史遗留问题,详见MossGrower对于该问题的解释。 但是对于null、arr以及obj的判断则全是object。对于Array、Object等一些构造函数的判断则是function。 因此,typeof操作符判断基本数据类型是准确的,但是对于复杂数据类型的判断是模糊的、不准确的。对于复杂数据类型的判断则需要第二种检测数据类型的方法。
第二种:instanceof 检测数据类型
instanceof的返回值只有两种,分别是true或false。判断的方式a instanceof b,判断a是否在b的实例上,即判断在b的原型对象上是否能找到a,能找到就会返回true,反之则返回false。
console.log(num instanceof Number); //false
console.log(str instanceof String); //false
console.log(boolean instanceof Boolean); //false
console.log(undefined instanceof Object); //false
console.log(null instanceof Object); //false
console.log(obj instanceof Object); //true
console.log(arr instanceof Array); //true
由此可见,instanceof并不适用于检测基本数据类型。
值得注意的是,undefined和null并不属于object。
注意:null并不是对象!
第三种:constructor 检测数据类型
constructor检测数据类型返回的是检测对象的构造函数。判断方式为xxx.constructor。xxx代指检测对象。
console.log(num.constructor);//ƒ Number() { [native code] }
console.log(str.constructor)//ƒ String() { [native code] }
console.log(boolean.constructor)//ƒ Boolean() { [native code] }
console.log(arr.constructor)//ƒ Array() { [native code] }
console.log(obj.constructor)//ƒ Object() { [native code] }
console.log(testFunction.constructor)//ƒ Function() { [native code] }
console.log(testClass.constructor)//ƒ Function() { [native code] }
console.log(Fun.constructor)//ƒ Function() { [native code] }
由此可见,constructor检测数据类型"挺全面的"。不论是简单数据类型还是复杂数据类型都可以检测出对应的数据类型。
但是constructor有一个缺点:就是constructor的原型是可以被修改的。详见以下样例:
// 数字类型
let num = 111;
console.log(num.constructor) //ƒ Number() { [native code] }
Number.prototype.constructor = Array.prototype.constructor//修改原型对象
console.log(num.constructor) // ƒ Array() { [native code] }
console.log(num.constructor==Number) // false
console.log(num.constructor==Array) // true
上述代码说明,原定义变量num本是数字类型,当我们将变量num的原型对象从Number更改为Array之后,变量num的数据类型不在与原数据类型Number一致。即在Number原型对象上找不到num。更改之后,我们便会在Array原型对象上找到num。
注意:null和undefined无法使用constructor检测,无法“点”出constructor方法,会报错。
综上所述,尽管constructor这种方式很全能,但是当我们更改原型对象时,也会导致检测不准确。因此我们有了第四种方式去检测数据类型。
第四种:Object.prototype.toString.call() 检测数据类型
通过前面三种检测数据类型的方式,我们发现它们都有各自的局限性。下面我将为大家介绍最标准的检测数据类型的方法。几乎可以判断任意的数据类型,废话不多说,直接上案例:
console.log(Object.prototype.toString.call(num)) // [object Number]
console.log(Object.prototype.toString.call(str)) //[object String]
console.log(Object.prototype.toString.call(boolean)) //[object Boolean]
console.log(Object.prototype.toString.call(arr))//[object Array]
console.log(Object.prototype.toString.call(obj))//[object Object]
console.log(Object.prototype.toString.call(testFunction))//[object Function]
console.log(Object.prototype.toString.call(new testClass))//[object Object]
console.log(Object.prototype.toString.call(new Fun))//[object Object]
console.log(Object.prototype.toString.call(new Date))//[object Date]
console.log(Object.prototype.toString.call(Math))//[object Math]
由此可见,相较于其他三种方法来说,Object.prototype.toString.call()可以很好的区分变量的数据类型。例如它可以区分出Number、String、Boolean、Array、Object、Function、Date、Math这些数据类型。
如果非要说这种方式的缺点,就是代码相较于其他三种方式有点长。
总结
1. typeof 检测数据类型
优点:可以准确的判断简单数据类型(Number、String、Boolean、undefined、null)
缺点:判断复杂数据类型比较模糊(Function、Object)
2. instanceof 检测数据类型
优点:可以准确的判断复杂数据类型(Array、Object)
缺点:不能准确的判断简单数据类型(Number、Boolean、String)
3. constructor 检测数据类型
优点:可以检测简单数据类型以及复杂数据类型
缺点:由于可以修改原目标原型对象可能会使检测结果出现偏差(无法检测null和undefined)
4. Object.prototype.toString.call() 检测数据类型
优点:可以检测所有数据类型
缺点:方法有点长