趁着整理之前写的JavaScript基础笔记, 把数据类型的四种检测方式彻底整明白了, 我觉得面试能把这些答上应该是没什么问题了.
本文内容: 介绍了四种检测方式, 实现了instanceof, 一个完美的数据类型检测函数
数据类型检测中的四种方法
tyopeof [value]
typeof是执行效率最高的方法, 它在底层是根据数据在内存存储的二进制值来判断他的数据类型的, 也因为这样的特性导致了它没办法分清
null
和object
- 000:对象
- 010:浮点数
- 100:字符串
- 110:布尔
- 1:整数
- 000: null(空指针)
-
由于对象的二进制值都是以000开头的, 而
null
因为全部位都为0,所以对象
与null
检测出来都为 “object”. -
Function
作为一种特殊的对象, 有他自己的特殊的属性(实现了call方法), JavaScript设计者认为有必要区分object
与function
, 并无设计缺陷上的问题. -
检测结果: “number / boolean / string / null / undefined / symbol / bigint / object”
-
还有一个需要注意到的点, 就是typeof检测返回的值是一个
string
类型, 所以嵌套的检测用于返回string类型
在C++
底层提供了特殊检测方法, 他按照存储值去检测数据类型, 所以它检测速度快效率高,但是没办法检测细分的对象类型
和null
[value] instanceof [constructor]
这个方法的本意是检测某个实例是否属于某一个类, 所以它没有办法检测原始值类型
检测原理:
-
如果构造函数上存在
[Symbol.hasInstance]
方法,则返回该函数的执行结果. -
如不存在, 则去
[value].__proto__
(原型链)上查找, 构造函数是否在[value]
的原型链上
从检测原理可以看出, 这个方法有他明显的缺点:
-
[Symbol.hasInstance]
可以改变instanceof 检测行为. -
prototype
属性是可以被重定向的, 所以可能导致结果不准确
那么, 怎么实现一个instanceof?
首先根据instanceof的检测原理, 我们可以得出instanceof的运行规律:
"右边的值有[Symbol.hasInstance]
方法吗?" 有 => 执行(行为1)
if (typeof rightVal[Symbol.hasInstance] === 'function') {
return rightVal[Symbol.hasInstance](leftVal)
}
"没有, 去左边的值的原型链上查找."(行为2)
let leftProto = Object.getPrototypeOf(leftVal)
while (leftProto) {
if (leftProto === rightVal.prototype) return true
// 如果找不到, 就往左值的下一级原型找
leftProto = Object.getPrototypeOf(leftProto)
}
return false
}
再加上一些检测参数的办法就完成拉 (▽)
function _instanceof(leftVal, rightVal) {
let leftType = typeof leftVal,
rightType = typeof rightVal,
objTypeReg = /^(object|function)$/
if (leftType == null || rightType == null) return false
if (!objTypeReg.test(leftType) || !objTypeReg.test(rightType)) return false
//行为1
if (typeof rightVal[Symbol.hasInstance] === 'function') {
return rightVal[Symbol.hasInstance](leftVal)
}
// 行为2
let leftProto = Object.getPrototypeOf(leftVal)
while (leftProto) {
if (leftProto === rightVal.prototype) return true
// 如果找不到, 就往左值的下一级原型找
leftProto = Object.getPrototypeOf(leftProto)
}
return false
}
[value].constructor === [constructor]
这个方法本意是为了访问实例的构造函数, 但是构造函数的指向也是可以被改变的,所以也不太好
Objcet.prototype.toString.call([value])
这个方法除了名字长点, 可以说是数据类型检测的最优解, 因为他可以兼容检测所有的数据类型, 包括对象的细分类型.
原理: toString在正常情况下是用来字符化值的, 但是在顶级对象Object
上的toString有一些不同, 它会去查找[value]
上的[Symbol.toStringTag]
属性, 如果存在则返回一个格式为: “[object ? ]” 的字符串.
名字长的解决方案:
let class2type = {}
toString = {}.prototype.toString // 简写
一个完美的类型检测函数:
function toType(obj) {
// 原始值
if (typeof obj !== 'function' && typeof obj !== 'object') return typeof obj
var instanceExp = /^\[object ([a-zA-Z]+)\]$/
var value = instanceExp.exec(toString.call(obj))[1] || 'object'
return value.toLowerCase()
}
感谢😘
如果觉得文章内容对你有帮助:
❤️欢迎关注点赞哦! 我会尽最大努力产出高质量的文章
个人公众号: 前端Link
联系作者: linkcyd 😁
往期: