在 JavaScript 中,很多时候我们需要对数据进行类型的判断;用于类型判断的方法也常常会被面试官给问到,那么今天我们就来探究一下在 JS 中是如何进行类型判断的,以及有哪些方法可以用于类型的判断。
谈到类型的判断,我们先来回想一下 JS 中的数据类型有哪些?
数据在v8中的存储
原始类型
在 JS 中,我们可以把原始类型分为以下五种;
- Number
- String
- Boolean
- undefined
- null
正是因为原始类型的值所占用内存都比较小 ,所以在 JS 中,原始类型都是被存储在调用栈中的。
引用类型
在 JS 中,常见的引用类型如下;
- Object
- Array
- Function
- Date
- RegExp
引用类型与原始类型不同,引用类型通常占用的内存都比较大;
而调用栈为了维护函数之间的调用关系且要保证v8引擎的执行效率,所以调用栈的存储空间比较小。
如果把引用类型也储存在调用栈中,就很容易造成爆栈的现象;所以引用类型一般是储存在堆这种数据结构中的 ,并且将堆中的引用地址存在调用栈当中。
数据的类型判断
typeof
在 JS 中,给我们打造了这样一种方法可以进行类型的判断,那就是 typeof
;先来看下面一段代码
ini
let str = 'hello'
let num = 123
let flag = false
let un = undefined
let nu = null
// 原始类型中,只有null会被typeof 判断成 object
console.log(typeof str);
console.log(typeof(num));
console.log(typeof(flag));
console.log(typeof(un));
console.log(typeof(nu)); // object
我们用 typeof
去判断原始类型时,可以看到 typeof
方法判断前面四种原始类型都能正确判断,而判断 null
时却输出了 object
。
这是因为 typeof
方法被打造出来的时候,它在判断时会把数据转换成二进制 ,而在当时的代码设定中,只要数据转换成二进制后的前三位只要是 0
,那就会被判断为是 object
,正好 null
这种原始类型转换为二进制后是 000000
,这就恰好导致了 typeof
会将null
判断成是 object
。
再来看,我们用 typeof
对引用类型进行判断;
vbnet
let obj = {}
let arr = []
let fn = function (){}
let date = new Date()
// 引用类型中,typeof 只能正确判断function这个引用类型,其他都默认是object
console.log(typeof(obj));
console.log(typeof(arr)); // object
console.log(typeof(fn)); // function
console.log(typeof(date)); // object
在上面的代码中,typeof
又可以判断 function
这种引用类型了。
当初 typeof
这个被打造出来时,也并不是用来判断引用类型的,typeof
就是被打造出来判断原始类型的 ,因为引用类型转二进制后大部分的前三位都是 0
,这才导致 typeof
会把绝大多数的引用类型判断成是 object
,而 function
也是一个特例,唯一一个能被 typeof
正确判断的引用类型。
了解到 typeof
方法的机制之后,我们可以去封装一个简单的判断对象的方法;只要是引用类型就返回object
(function
除外):
javascript
function isObject(obj) {
if (typeof obj === 'object' && obj !== null) {
return true
}
return false
}
console.log(isObject({}));
instanceof
我们再来看 instanceof
这个方法,也是可以用于类型的判断;instanceof
的用法是判断左边是否隶属于右边:来看下面代码
javascript
let str = 'hello'
let num = 123
let flag = false
let un = undefined
let nu = null
console.log(str instanceof String);
console.log(num instanceof Number);
console.log(flag instanceof Boolean);
可以看到 instanceof
并不能判断原始类型;再来看引用类型:
javascript
let obj = {}
let arr = []
let fn = function (){}
let date = new Date()
console.log(obj instanceof Object);
console.log(arr instanceof Array);
console.log(fn instanceof Function);
console.log(date instanceof Date);
当我们用 instanceof
判断引用类型时,又能够准确的判断出来,这是为什么呢?
其实这是因为,instanceof
方法是通过原型链查找的 ,每个引用类型 都可以看作是通过 new
和构造函数的方式创建出来的实例 ,这时 instanceof
就会判断左边实例对象的隐式原型(也就是构造函数的显式原型prototype
属性)是否出现于右边的原型链上。
javascript
console.log(arr instanceof Object); // 通过原型链查找到了object
console.log(1 instanceof Number); // 原始值
let num1 = new Number(1)
console.log(num1 instanceof Number); // Number 包装类对象
知道了 instanceof
的原理后,我们也可以去手动实现一下它的功能;
javascript
// function myInstanceof(L, R) {
// // 如果对象的原型不存在,则对象不是构造函数的实例
// if(L == null || L.__proto__ == null){
// return false
// }
// // 检查对象的原型是否与构造函数的 prototype 相同
// if (L.__proto__ === R.prototype) {
// return true
// }
// // 递归地检查对象的原型的原型
// return myInstanceof(L.__proto__, R)
// }
// 函数 myInstanceof 用于检查对象 L 是否是构造函数 R 的一个实例
function myInstanceof(L, R) {
// 当 L 不等于 null 时,继续执行循环。这是因为 null 是原型链的末端,表示不再有父类
while (L!== null) {
// 检查对象 L 的原型链中的原型(__proto__)是否等于构造函数 R 的原型(prototype)。如果两者相同,意味着对象 L 是构造函数 R 的实例
if (L.__proto__ == R.prototype){
return true
}
// 将对象 L 替换为其原型链中的下一个原型(__proto__),以便继续检查其是否是构造函数 R 的实例
L = L.__proto__
}
return false
}
Object.prototype.toString.call()
基本在每个数据类型的身上都会有一个方法,那就是toString()
,但不同类型的数据身上的toString
方法也是不同的;
vbscript
let a = {}
let b =[1,2,3]
let c = 1
let d = '123'
let e = true
console.log(a.toString());
console.log(b.toString());
console.log(c.toString());
console.log(d.toString());
console.log(e.toString());
对此,toString()
方法,可以分为以下;
- 对象的
toString()
- 数组的
toString()
:将数组中的元素用逗号的方式拼接成字符串 - 其他的
toString()
:直接将值修改成字符串字面量
Object.prototype.toString()
我们再来看看对象原型上的toString
方法是什么样的?
在官方文档的介绍中,我们可以得出Object.prototype.toString()
的基本用法;
Object.prototype.toString()
-
如果
toString
接收的值是undefined
,则返回"[object Undefined]"。 -
如果
toString
接收的值是null
,则返回"[object Null]"。 -
如果接收的既不是
undefined
也不是null
的话,就调用ToObject(x)
将x
转为对象 ,此时得到的对象内部一定拥有一个属性[[class]]
,而该属性[[class]]
的值就是x
的类型- 设 class 是
[[class]]
的值 - 返回由
"[object"
和class
和"]"
拼接得到的字符串
- 设 class 是
这也是为什么对象的toString
方法会返回[object Object]
的原因。
那知道了Object.prototype.toString()
方法的机制之后,我们就可以利用这个机制去进行类型的判断;
javascript
let a = {}
let b =[]
console.log(Object.prototype.toString(a));
console.log(Object.prototype.toString.call(b)); // this 指向
正是因为,Object.prototype.toString()
在接收到非undefined
和null
的值后,就会返回一个带有自身类型的字符串 ,所以我们就可以利用.call
方法去让传进来的值触发掉Object.prototype.toString()
这个方法。这样我们也能达成一个类型判断的效果。
通过这个方法,我们也能手搓出一个type
方法来返回数据类型;
javascript
function type (x) {
let res = Object.prototype.toString.call(x)
return res.slice(8, -1) // 从后往前,最后一个不要
}
console.log(type('123'));
Array.isArray()
最后,要介绍一个特殊的类型判断的方法 Array.isArray()
,它只能用于判断类型是否为数组;
ini
let arr = [];
let obj = {};
let n = 123;
console.log(Array.isArray(arr));
console.log(Array.isArray(obj));
console.log(Array.isArray(n));
以上就是,在 JS 中能够进行数据类型判断的方法了,觉得有用的话就点点赞吧!