数据类型检测及重写instancof

本文详细探讨了JavaScript中typeof和instanceof运算符用于数据类型检测的原理和局限性。typeof返回数据类型的字符串,但无法区分null和对象。instanceof则用于检测实例是否属于某个类,但它无法应用于原始值类型。此外,constructor属性和Object.prototype.toString.call方法也被提及作为辅助的类型检测手段,但它们都有其局限性。文章最后提出了一个自定义的instance_of函数以改进instanceof的检测方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  1. typeof 检测数据类型的运算符

    • 返回结果是一个字符串,字符串包含了对应的数据类型’number/string/boolean/undfined/symbol/bigint/object/function’
    • typeof typeof xxx 结果都是: ‘string’

    「小bug」typeof的原理,按照计算机底层存储的二进制结果来进行检测的,对象都是以000…开始的

    • typeof null ‘object’ null的二进制存储值000
    • 所有对象都是以000开始的,所以基于typeof检测的结果都是’object’,也就是typeof无法细分是普通对象还是数组对象
function* fn(){
    yield 1;
}
console.log(typeof fn)//'function'
  1. instancof
    • 并不是用来检测数据类型,是用来检测当前实例是否属于这个类,只是被‘打肿脸充胖子了’
    • 用它来检测,一般只应用于普通对象/数组对象/正则对象/日期对象等的具体细分的
    • 无法应用到原始值类型数据的检测上
let arr = []
console.log(arr instancof Array)//true
console.log(arr instancof Object)//true 绝对不能证明是true就是普通对象
console.log(arr instancof Reg)//false
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 instancof Number)//false
console.log(m instancof Number)//true

function Person () {}
Person.prototype = Array.prototype
let p1 = new Person
console.log(p1)//虽然p1可以基于__proto_找到Array.prototype,但是它不具备数组的任何特征(length/索引都没有的),所以断定它一定不是一个数组
console.log(p1 instancof Array)//true

console.log(arr instancof Array)//true
console.log(Array[Symbol.hasInstance](arr))//true
//原理 基于‘实例 instanceof 类’检测的时候,浏览器底层是这样处理的‘类[Symbol.hasInstance](实例)’
// Function.prototype[Symbol.hasInstance] = function [Symbol.hasInstance](){[native code]}
// Symbol.hasInstanc方法执行的原理
    // 根据当前实例的原型链上(_proto__)是否存在这个类的原型(prototype)
    // arr.__proto__ === Array.prototype  arr instancof Array : true
    // arr.__proto__ === Object.prototype arr instancof Object : true

let obj = {}
console.log(arr instancof obj)//Uncaught TypeError: Right-hand side of 'instancof' is not called,报错原因是因为obj是一个对象,他没有[Symbol.hasInstance]这个属性,(函数才可以调用Function.prototype上的这个方法)
  1. constructor
    • 一样是打肿脸充胖子,原本就是获取实例的构造函数的,基于这些特点可以充当数据类型检测
    • 比instancof还好用一点
    • 但是也不准确:constructor是可以随意被修改是
console.log(arr.constructor === Array)//true 在constructor不被修改的情况下,这样区分是数组还是普通对象
console.log(arr.constructor === Object)//true
console.log(arr.constructor === Regexp)//false

console.log(p1.constructor === Array)//true 一旦原型重定向,constructor也改了,所以也就不准了

console.log(n.constructor === Number)//true
console.log(m.constructor === Number)//true
  1. Object.prototype.toString.call([value])或者({}).prototype.toString.call([value])
    • 专门用来检测数据类型的(很强大很暴力的一种办法,基本零瑕疵)
    • Number/String/Boolean/Symbol/BigInt/Function/Array/Regexp/Date/Object…的原型上都有toString,除了Object.prototype.toString不是用来转换为字符串的,其余都是,Object.prototype.toString是用来检测数据类型的
    • 返回结果’[Object 当前对象[Symbol.toStringTag]||对象.构造函数(不受自己更改的影响,对内置类有效)||Object]‘
class Person(){
    // 只要获取实例的[Symbol.toStringTag]属性值,则调用这个方法
    get[Symbol.toStringTag](){
        return 'Person'
    }
}
let p1 = new Person
console.log(Object.prototype.toString.call(p1))//'[object pePerson]'
重写instancof
//obj要检查的实例对象(不支持原始值类型)
// constructor要检测的类(必须是个函数
function instance_of(obj,constructor){
    //参数校验
    if(obj == null || !/^(object|function)$/.test(typeof obj)) return false
    if(typeof constructor !== 'function') throw new TypeError('Right-hand side of 'instanceof' is not callable)
    //obj.__proto__ === Object.getPrototypeOf(obj)
    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.getPrototype(proto)
    }
}
console.log(instance_of([],Array))//true
console.log(instance_of([],Object))//true
console.log(instance_of([],RegExp))//false
console.log(instance_of(10,Number))//fasle
console.log(instance_of(new Number(10),Number))//true
console.log(instance_of([],{}))//报错
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值