常见的判断js数据类型的方法有以下几种:
typeof操作符
typeof()能够返回每一种数据到底是什么类型的,能够区分数据与数据之间的不同。
typeof可以返回以下几种类型:
typeof返回的结果是string类型的
-
'number'
-
'string'
-
'boolean'
-
'undefined'
-
'object '(不是单纯的指对象,而是泛泛地指引用类型,例如对象{},数组[],null)
那null不是原始类型吗? => 因为最早的时候,null是代替空对象出现的,给对象占位的。但是null绝对不是一个object
-
'function'
-
'symbol'
-
'bigint'
typeof有两种写法:可以在后面加括号,也可以在后面加空格
typeof(123) //number
typeof('123') //string
typeof(null) //object
typeof({}) //object
typeof([]) //object
typeof function(){} //function
typeof undefined //undefined
typeof Symbol() //symbol
typeof 123n //bigint
typeof(typeof a) //string typeof a返回的是’undefined‘
typeof的问题就是,无论传入的对象是什么类型的,都返回'object'。
总的来说,typeof对对象类型的值不能准确地判断出类型,但是能够准确地判断基本数据类型的值。
因此引入了instanceof操作符。
instanceof
语法 A instaceof B,判断A是否为B的实例
内部机制是判断对象和构造函数在原型链上是否有关系,如果有关系,返回真,否则返回假。
function Person(){
}
let person = new Person()
console.log(person instanceof Person); //true
console.log(person instanceof Object); //true
基本数据类型的值不能用instanceof判断类型,但是用基本数据类型包装类创建的对象可以用instanceof判断类型。
"123" instanceof String //false
true instanceof Boolean //false
123 instanceof Number //false
new String() instanceof String //true
new Boolean() instanceof Boolean //true
new Number() instanceof Number //true
[1,2,3] instanceof Array //true
[1,2,3] instanceof Object //true
null instanceof Object //false
let obj = {}
obj instanceof Object //true
function test(){}
test instanceof Function //true
test instanceof Object //true
instanceof 可以在通过原型链继承的继承关系中用来判断一个实例是否属于它的父类型
function Father(){}
function Son(){}
Son.prototype = new Father()
let son = new Son()
son instanceof Son //true
son instanceof Father //true
son instanceof Object //true
A instanceof B 的判断逻辑是:
A的__proto__一层一层往上,是否对应到B.prototype
手写instanceof
// 手写instanceof
function myInstanceof(obj,constructor){
// 先判断是否为基本数据类型,如果是则直接返回false
// 如果为null也要返回false
if(typeof(obj) !== 'object' || obj === null) return false
let proto = obj.__proto__
while(true){
if(proto === constructor.prototype) return true
// 继续往深处找
proto = proto.__proto__
}
return false
}
缺点:
-
不能检测基本数据类型
-
原型链可能被修改,导致检测结果不准确
-
只要能在原型链上找到构造函数,就返回true,所以类型可能不准确
constructor
constructor的判断方法跟instanceof的判断方法相似,但是constructor还可以处理基本数据类型的检测。
语法:A.constructor === B
查看对象对应的构造函数,不会探究其原型链上的构造函数
let num = 123
console.log(num.constructor === Number); //true
let str = '123'
console.log(str.constructor === String); //true
let obj = {}
console.log(obj.constructor === Object); //true
let arr = []
console.log(arr.constructor === Array); //true
console.log(arr.constructor === Object); //false
console.log(null.constructor === Object); //报错
console.log(undefined.constructor === Object); //报错
undefined和null是无效的对象,不会有constructor存在。
function Father(){
}
function Son(){
}
Son.prototype = new Father()
let son = new Son()
console.log(son.constructor === Son); //false
console.log(son.constructor === Father); //true
console.log(son.constructor === Object); //false
引入修改了Son的原型,所以检测不到
缺点:
-
null和undefined没有constructor,导致会报错
-
constructor会被改写,导致检测结果不正确
Object.prototype.toString.call()
Object.prototype.toString()
返回"[object Type]"
,这里的Type
是对象的类型。如果对象有 Symbol.toStringTag 属性,其值是一个字符串,则它的值将被用作Type
。许多内置的对象,包括 Map 和 Symbol,都有Symbol.toStringTag
。
对于Object对象,直接调用toString()就能返回[object Object],除了 Object 类型的对象外,其他类型直接使用 toString
方法时,会直接返回都是内容的字符串。
所以我们需要使用call或者apply方法来改变toString方法的执行上下文。
let str = '123'
console.log(str.toString()); //'123'
let arr = [1,2,3]
console.log(arr.toString()); //'1,2,3'
let obj = {}
console.log(obj.toString()); //'[object Object]'
function test(){}
console.log(test.toString()); //'function test(){}'
console.log(Object.prototype.toString(str)); //'[object Object]'
console.log(Object.prototype.toString(arr)); //'[object Object]'
console.log(Object.prototype.toString(obj)); //'[object Object]'
console.log(Object.prototype.toString(test)); //'[object Object]'
toString这个方法,返回值的格式原本就应该是[object type],type代表的是调用这个方法的数据的数据类型。
而直接返回内容的字符串其实是js重写了某些数据类型的toString()方法。
而call改变了this的指向,用来调用未被重写的toString()方法。
let str = '123'
console.log(str.toString()); //调用的是重写后的toString方法
// 那我们删掉此方法
delete String.prototype.toString
console.log(str.toString()); //[object String]
但是想要实现此方法,不可能删除所有的toString方法,所以使用call或者apply来改变this指向,让其能够直接调用未被重写的toString方法。
并且Object是所有原型链的顶端,所有直接使用Object.prototype.toString.call()
function test(){}
Object.prototype.toString.call(123) //[object String]
Object.prototype.toString.call('123') //[object Number]
Object.prototype.toString.call(test) //[object Function]
Object.prototype.toString.call([]) //[object Array]
Object.prototype.toString.call({}) //[object Object]
Object.prototype.toString.call(null) //[object Null]
Object.prototype.toString.call(undefined) //[object Undefined]
此方法是判断数据类型最准确的方法。
等比较
严格等于===
与某个固定值比较,并返回一个布尔值
undefined === null //false
undefined == null //true
Object.property.isPrototypeOf()
与instanceof相似,都是基于原型链和原型对象去做判断的,用来检查一个对象是否存在另一个对象的原型上。
function Father(){
}
function Son(){
}
Son.prototype = new Father()
let son = new Son()
let father = new Father()
Son.prototype.isPrototypeOf(son); //true
Father.prototype.isPrototypeOf(son); //true
Object.prototype.isPrototypeOf(son); //true
Array.isArray
专门用于检测数组类型的。
Array.isArray([]) //false
Array.isArray({}) //true
Number.isNaN
此方法是基于window.isNaN而进行完善的。
用于判断值是否为NaN
而NaN具有一个特性,它不等于任何值,包括它自身,及NaN == NaN的返回值为false
window.isNaN(NaN) //true
window.isNaN('abc') //true
但是window.isNaN()内部是将值先转换成数字类型,再进行判断,而Number('abc')转换为NaN,所以判断为true
而这中方法不是很演技,所以ES6提供了Number.isNaN
Number.isNaN(NaN) //true
Number.isNaN('abc') //false