数据类型检测: 怎么实现一个 instanceof ?

趁着整理之前写的JavaScript基础笔记, 把数据类型的四种检测方式彻底整明白了, 我觉得面试能把这些答上应该是没什么问题了.

本文内容: 介绍了四种检测方式, 实现了instanceof, 一个完美的数据类型检测函数


数据类型检测中的四种方法

tyopeof [value]

typeof是执行效率最高的方法, 它在底层是根据数据在内存存储的二进制值来判断他的数据类型的, 也因为这样的特性导致了它没办法分清nullobject

  • 000:对象
  • 010:浮点数
  • 100:字符串
  • 110:布尔
  • 1:整数
  • 000: null(空指针)
  1. 由于对象的二进制值都是以000开头的, 而null因为全部位都为0,所以对象null检测出来都为 “object”.

  2. Function 作为一种特殊的对象, 有他自己的特殊的属性(实现了call方法), JavaScript设计者认为有必要区分objectfunction, 并无设计缺陷上的问题.

  3. 检测结果: “number / boolean / string / null / undefined / symbol / bigint / object”

  4. 还有一个需要注意到的点, 就是typeof检测返回的值是一个string类型, 所以嵌套的检测用于返回string类型

C++底层提供了特殊检测方法, 他按照存储值去检测数据类型, 所以它检测速度快效率高,但是没办法检测细分的对象类型null

[value] instanceof [constructor]

这个方法的本意是检测某个实例是否属于某一个类, 所以它没有办法检测原始值类型

检测原理:

  1. 如果构造函数上存在[Symbol.hasInstance] 方法,则返回该函数的执行结果.

  2. 如不存在, 则去[value].__proto__(原型链)上查找, 构造函数是否在[value]的原型链上

从检测原理可以看出, 这个方法有他明显的缺点:

  1. [Symbol.hasInstance]可以改变instanceof 检测行为.

  2. 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 😁
往期:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值