JS中四种数据类型检测底层机制

114 阅读3分钟

typeof 检测数据类型的运算符

返回结果是一个字符串,字符串中包含了对应的数据类型 "number/string/boolean/undefined/symbol/bigint/object/function"

typeof不能细分对象,不能区分null和object。原理:所有数据类型值在计算机存储都是按照“二进制”存储的,typeof检测的时候,是按照计算机存储二进制的值来检测的。

只要是对象都是以 000 开始的,而null -> 000000,所以typeof null => 'object'。

函数 function不是以000开始的,所以typeof function(){} => 'function'。

    // 多个typeof 检测 结果都是'string'
    typeof typeof xxx  // 结果为'string';

    function* fn(){
        yield 1;
    }
    console.log(typeof fn) // 'function'

    typeof NaN => 'number'
    typeof null=> 'object'
    typeof {} => 'object'
    

instanceof

  • 并不是检测数据类型的,是用来检测当前实例是否属于这个类,只是被“打肿脸充胖子了”

  • 用它来检测,一般只应用于普通对象/数组对象/正则对象/日期对象等的具体细分的

    //存在的问题
    let arr = []
    console.log(arr instanceof Object)   // true 此结果不能证明 是普通对象
    console.log(arr instanceof Array) // true  
    console.log(arr instanceof Reg)   // false
    
    console.log(arr instanceof Array) // true  
    =>
    console.log(Array[Symbol.hasInstance](arr)) // true
    // 基于 “实例 instance 类” 检测的时候,浏览器底层是这样处理的 “类[Symbol.hasInstance](实例)”
    // Function.prototype[Symbol.hasInstance] = function [Symbol.hasInstance]  {[native code]}
    // Symbol.hasInstance 方法执行的原理
    //  根据当前实例的原型链上(__proto__)是否存在这个类的原型(prototype)
    // arr.__proto__ === Array.prototype => arr instanceof Array : true
    // arr.__proto__.__proto__ === Object.prototype => arr instanceof : Object true
    
    
    
    // instanceof 无法应用到原始值类型数值的检测上的
    let n = 10
    let m = new Number(10)
    console.log(n.toFixed(2)) // 10.00 n是Number类的实例,只不过它是字面量方式创造出来的原始类型值而已 其实n.toFixed(2)的时候,n这个基本类型值 浏览器内部也会把它 Object(n) 一下,然后再调用方法,因为此时他就具备了__proto__
    console.log(m.toFixed(2)) // 10.00 m也是Number类的实例,只不过它是‘构造函数’创造出来的‘引用’类型值而已
    console.log(n instanceof Number) // false
    console.log(m instanceof Number) // true
    
    function Person(){}
    Person.prototype = Array.prototype
    let p1 = new Person();
    console.log(p1) // 虽然p1 可以基于__proto__ 找到Array.prototype,但是他不具备数组的任何特征(length/索引都没有的),所以断定这厮一定不是一个数组
    console.log(p1 instanceof Array) // true
    

constructor

  • 一样的打肿脸充胖子,原本就是获取实例的构造函数的,基于这些特点可以充当数据类型检测

  • 不过比instanceof好用一点

  • 但是也不准确 因为constructor也可以随意修改

    let arr = []
    console.log(arr.constructor === Object)   // false 
    console.log(arr.constructor === Array) // true   在constructor不被修改的情况下,这样区分是数组还是普通对象。是准确的。
    console.log(arr.constructor ===  Reg)   // false
    
    
    function Person(){}
    Person.prototype = Array.prototype
    let p1 = new Person();
    console.log(p1.constructor === Array) // true  一旦原型对象改了,constructor也就改了。就不准确了。
    
    
    let n = 10
    let m = new Number(10)
    console.log(n.constructor ===Number) // true  这个可以的
    console.log(m.constructor ===Number)  // true
    

Object.prototype.toString.call([value]) 或者 ({}).toString.call([value])

  • 专门用来检测数据类型的(很强大很暴力的一种方式,基本零瑕疵,除了代码有点长外)

  • 含有toString的类:Number String Boolean Array Symbol BinInt RegExp Date Object....的原型上都有toString,除了Object.prototype.toString 不是转换字符串的,其余都是。Object.prototype.toString是用来检测数据类型的。

  • NodeList 和 HTMLCollection 原型上没有toString 可以直接调用Object的toString

  • 返回结果 "[object 对象[Symbol.toStringTag]||对象.构造函数(不受自己更改的影响,对内置类有效)||Object]"

    let class2type = {},
        toString = class2type.toString;    //Object.prototype.toString
    
        toString.call(function* (){})  //"[object GeneratorFunction]"
        toString.call(document.querySeletorAll('*'))  //"[object NodeList]"
    
    
    class Person{
    
    }
    let p1 = new Person; 
    
    toString.call(p1)  // "[object Object]"
    
    
    class Person{
        // 只要获取实例的 [Symbol.toStringTag] 属性值,则调用这个方法
        get[Symbol.toStringTag](){
            return "Person"
        }
    }
    let p1 = new Person; 
    
    toString.call(p1)  "[object Person]"
    

重写instanceof

// + obj 要检测的实例对象 (不支持原始值类型)
// + constructor 要检测的类 (必须是一个函数)
function _instanceof(obj,constructor){
    if(obj == null || /^(object|function)$/i.test(typeof obj)) return false
    if(typeof constructor !== 'function')  throw new TypeError("Right-hand side of 'instanceof' is not callable")

    //let proto = obj.__proto__,  IE不支持
    let proto = Object.getPrototypeOf(obj),

        prototype = constructor.prototype;

    while(true){
        // 找到Object.prototype.__proto__都没有相等的,则证明不是当前类的实例
        if(proto === null) return false
        // 找到对象的原型链包含类的原型,则证明对象是类的一个实例
        if( proto === prototype) return true
        // 一级级查找即可
        proto = Object.getPrototypeOf(proto)
    }

}

console.log(_instanceof([],Array)) // true
console.log(_instanceof([],Object)) //true
console.log(_instanceof([],RegExp)) //false
console.log(_instanceof(10,Number)) //false
console.log(_instanceof(new Number(10),Number)) //true
console.log(_instanceof([],{}])) //报错 

知识点

let obj = {}
obj.__proto__ === Object.getPrototypeOf(obj)