深入 JS 类型检测原理和模拟实现

原文地址

掘金

求 star

思维导图

未命名文件.jpg

JS 中的类型

  • 基本类型,number,string,null,undefined,Boolean,es6 新增的 symbol,es11
    中的 bigint 总共 7 种基本类型
  • 引用类型:object,function

数据类型的检测方式

数据类型的检测的方法中共有四种typeof, instanceof, Object.prototype.toString.call(), constructor

一、typeof

typeof 检测原理是:在计算机底层 根据js数据类型的二进制的值进行检测的

  • typeof 检测类型后的返回值是一个字符串,ES5 中对于一个为定义的变量判断类型也会抛出字符串undefined,而不是报错。但是用 let, const 声明的变量也能会导致暂时性死区,抛出 ReferenceError
typeof undefined    //"undefined"
typeof a // "undefined"
typeof b // "ReferenceError" 
let b

typeof 12   //"number"
typeof NaN  //"number"
typeof ''   //"string"
typeof 1n    //"bigint"
typeof function(){}     //"function"
  • (缺点) typeof 可以检测除了 null 类型以外的数据型。null 被检测成 object 这是一个历史遗留的 bug。
  • (缺点) typeof 不能检测出具体的 object 类型,因为对象类型的二进制开头都是 000。比如 typeof [] //"object" 检测数组,正则,日期等。
  • 其他类型的二进制,000 对象,00000... null,1 整数,010 浮点数 ,100 字符串,110 布尔值 , -2^30 undefined
typeof {}   // "object"
typeof []   // "object"
typeof /^/  // "object"

typeOf 判断函数为什么可以判断出是 function 类型而不是 object

JS 中虽然函数也是对象,但是 typeOf 判断函数是会调用 call 方法来判断。所以能判断出function

思考:typeof null 检测出来的结果为什么是 object

typeof 是根据二进制值来判断数据类型的,null 的二进制值是 000,而 object 类型的二进制值都是 000 开头的,所以 typeof 检测 null 是也被检测成 object,这是一个历史里留下来的bug

思考:0.1 + 0.2 !== 0.3 成立原因

二、滥竽充数的 instanceof

instanceof 检测机制是:判断右侧的类型是否出现在左侧实例的原型链上,出现了返回的结果就是true 否则是 false

  • instanceof 判断返回的结果是一个 Boolean 值。
  • instanceof 是用来检测当前的实例是否属于某个类的。可以用来解决 typeof 无法检测具体的对象类型的问题。
let ary = []
console.log(ary instanceof Array)   // true

let reg = /^/
console.log(reg instanceof RegExp)  // true
  • (缺点) 只要当前类出现在实例的原型链上检测的结果就是 true,那么 Object 类型检测的结果永远都是 true,如果实例的原型被修改了,即使检测的结果是 true,也是不准确的。
  • (缺点) instanceof 不能检测基本数据类型。
// 出现在原型链上的类型都被判断成true
let ary = []
console.log(ary instanceof Object)   // true

function fn(){}
fn.prototype = Array.prototype  // 原型被修改了
let f = new fn()
console.log(f instanceof Array) // true
console.log(f instanceof Function) // false

// 不能检测基本数组类型
console.log(1 instanceof Number)    //false
console.log('' instanceof String)   //false
console.log(false instanceof Boolean)   //false

思考,模拟实现 instanceof

思路:根据左侧实例的原型链上是否出现右侧的类型进行判断结果。即实例.__proto__ === 类.prototype,则是true 否则是 false。对原型和原型链不熟悉的可以看 面试 | 你不得不懂得 JS 原型和原型链。

function _instanceof(example, classP) {
    let proto = Object.getPrototypeOf(example),
        classPrototype = classP.prototype
    while (true) {
        if (proto === classPrototype) {
            return true
        }
        if (proto === null) {
            return false
        }
        proto = Object.getPrototypeOf(proto)
    }

_instanceof([], Array)  //true
_instanceof('', Array)  // false
_instanceof('', Object) // true

三、滥竽充数的 constructor

instanceof 检测机制是:constructor 存放的就是构造函数本身,通过存放的构造函数来检测类型。不熟悉 constructor的也可以看 面试 | 你不得不懂得 JS 原型和原型链。

  • constructor 可以检测基本类型,这点比 instanceof 好用
let ary = []
ary.constructor === Array   // true,ary.constructor ==> Array.prototype.constructor
ary.constructor === String  // false
ary.constructor === Object  // false

let a = 1
a.constructor === Number    // true

let s = ''
s.constructor === String    // true
  • (缺点) constructorinstanceof 有一样的缺陷,就是 constructor 可以被修改也就是重定向
Number.prototype.constructor = 'abc'
let a = 1
a.constructor === Number    // false

四、Object.prototype.toString.call(),标准检测类型

检测机制是:利用 Object.prototype.toString 返回实例所属类的信息,通过改变 toString 中的 this 指向来返回指定参数的类型。

  • Object.prototype.toString 返回的格式固定是 '[object xxx]',可以检测任意类型
Object.prototype.toString.call(1)       //"[object Number]"
Object.prototype.toString.call('')      //"[object String]"
Object.prototype.toString.call(null)        //"[object Null]"
Object.prototype.toString.call(undefined)       //"[object Undefined]"
Object.prototype.toString.call(Symbol())        //"[object Symbol]"
Object.prototype.toString.call([])          //"[object Array]"
Object.prototype.toString.call(function(){})        //"[object Function]"
Object.prototype.toString.call({})      //"[object Object]"
  • (缺点) 不能直接提取出数据类型,还需要转变才拿到直接的类型

五、其他快捷判断方式

isNumber(), isNaN(), Array.isArray()。。。

六、封装一个万能的检测方法 toType()

1. jQuery 中的类型检测

(function () {
    // jquery 中的 toType
    let class2type = {},
        toString = class2type.toString

    // 设定类型映射表
    let arrType = ['Number', 'String', 'Boolean', 'Symbol', 'Array', 'Date', 'Error', 'RegExp', 'Function', 'Object']
    arrType.forEach(item => {
        class2type[`[object ${item}]`] = item.toLowerCase()
    });

    function toType(checkType) {
        if (checkType === null || checkType == undefined) {
            return checkType + ''
        }
        return typeof checkType == 'object' || typeof checkType == 'function' ? class2type[toString.call(checkType)] : typeof checkType
    }
    window.toType = toType
})()

toType(1)   //"number"
toType('')  //"string"
toType(null)    //"null"
toType(undefined)   //"undefined"
toType({})  //"object"
toType(function(){})    //"function"
toType([])  //"array"

本质上还是利用了Object.prototype.toString.call()

2. 自封装一个更简单的 selfType()

(function () {
    function selfType(checkType) {
        let res = {}.toString.call(checkType).split(' ')[1].split(']')[0].toLowerCase() // 可以使用正则
        return res
    }
    window.selfType = selfType
})()

selfType('')        //"string"
selfType(1)     //"number"
selfType(null)      //"null"
selfType(undefined)     //"undefined"
selfType(function(){})     //"function"
selfType({})    //"object"

你可能感兴趣的:(深入 JS 类型检测原理和模拟实现)