经过这段时间惨烈的秋招经历,我发现我对于一些问题的理解往往不够深入且全面,导致我在回答的时候没办法形成调理,狠狠的吃了好多亏。基于这些,我痛定思痛的对某些我踩过坑的问题进行梳理。
言归正传,我们本次要讲的问题是类型判断。但在这之前,我们需要先了解一下他的前置知识:数据类型。在面试中,相信大家都被问过如下问题:
请讲一下你对于javascript中的数据类型的理解
这个问题可以说是前端面试中最最经典的问题,首先JavaScript是一个弱类型语言,之所以说他是弱类型语言,是因为它不需要提前声明变量的类型,在程序运行的过程中他会自动去定义,也就是说明你可以使用一个变量保存不同类型的值。
var foo = 1
foo = 'why'
foo = false
这种语言特性虽然给予了我们非常多的便利,但是在实际开发过程中却给我们带来的许多类型错误,也会带来一些安全性的问题。基于这个问题微软开发了ts语言作为js的一个加强和补充。
JS中的数据类型分为基础数据类型和引用数据类型。基础的数据类型有NULL,Undefinded,Boolean,String,Number,BigInt和Symbol,而引用数据类型则是object。
那么在基本数据类型中,null和undefinded对于新手来说很难以区分。在定义上来说,null表示一个空对象指针,而undefined表示未定义的变量。在JavaScript的设计里,null表示的是一个无的对象,转为数值是0;undefined则时表示一个‘无’的原始值,转为数值是为NaN。
但是我们在使用typeof null的时候,他却会返回一个‘object’类型。这一点虽然是一个js的设计问题,但是从这里展开,我们就可以讲一讲几个常见的类型顶判断的方式和原理
1.typeof
在所有的类型判断中,就属typeof最为经典,但是它最出名的地方却在于他不中用。
//我们来看看如下代码:
console.log(typeof 1) //number
console.log(typeof '1') //string
console.log(typeof a) //undefined
console.log(null) //object
上文我们已经提到过,这些全部都属于JavaScript的基本数据类型,但是在这里,typeof却会把null的类型返回为object。 其原因就在于,typeof会将所有传进去的值全部转换为二进制,而当年js在定义原始类型的时候,要求被转为二进制的前三个值绝对不为0。因此对于二进制前三位为零的值,js将他们一律认定为对象。
偏偏null又是js语言后来引进的,基于其它语言的定义,js将null的二进制值定义为为一串0。因此就出现了这样一个由官方团队创造出来的bug。
2.instanceof
instanceof只能判断引用类型,它的原理是通过原型链查找来进行类型判断。 具体来说就是,instanceof会顺着原型链查找出其继承的原型,看看原型到底是属于字符型、数值型还是别的什么具体类型,如果判断的类型与我们期待的类型相同的话,则输出true,否则则输出false。
完美的判断方式:Object.prototype.toString.call()
在我平时的使用中,我认为使用范围最广的一种方式就是:Object.prototype.toString.call(),它是一种基于原型上的方法,而且不像前两种各有各的局限性,Object.prototype.toString.call()可以正确的判断出每一种类型。
let s = '123'
let n = 123
let nu = null
let sy = Symbol(123)
let arr = []
let fn = function(){}
let obj = {}
console.log(Object.prototype.toString.call(s)) //[object String]
console.log(Object.prototype.toString.call(n)) //[object Number]
console.log(Object.prototype.toString.call(nu)) //[object Null]
console.log(Object.prototype.toString.call(sy)) //[object Symbol]
console.log(Object.prototype.toString.call(arr)) //[object Array]
console.log(Object.prototype.toString.call(fn)) //[object function]
console.log(Object.prototype.toString.call(obj)) //[object Object]
关于他的原理则是:
- 当我们传值进去为Undefined或者null的话,方法直接返回Undefined或者null
- 当我们传值不为Undefined或者null,那么js将要调用ToObject方法,将0作为ToObject(this)的执行结果
- ToObject的执行原理则是根据传进去的值,将其转换为对象,并根据值的类型对他命名
- 定义一个class作为内部属性的值
- 将ToObjectd的值和class的值拼接起来,也就得到了如上的结果
当然,大家可能还有个疑问,就是这里为什么要带上call()方法呢?一般来说,call()方法是用来将前一个函数的this指向obj,在这里呢,call(0方法确保了此处的类型判断是原型在调用,从而方便输出正确的值。