JavaScript 数据类型详解:typeof 的 bug 与类型判断的终极方案

155 阅读3分钟

数据类型

JavaScript中数据类型主要被分为两个大类:简单数据类型与引用数据类型

简单类型

  • 常见简单类型:Number,String,Boolean,undefined,null,Symbol,Bigint
  • 特点:不可向简单类型对象上添加属性与方法

引用类型

  • 常见引用类型: Function,Array,Object,Set等等
  • 特点:引用类型对象不直接保存对象的值,而是保存对象在堆中的地址

类型判断

使用typeof()函数判断类型

简单类型

null会被错误的判断为Object类型,除此之外的简单数据类型正常判断

41fd18f34a35708bcd3d1ee132d5c48d.png

引用类型

几乎所有引用类型都被判断为Object类型,Function除外

c50c20e37e3b7c05c96b58b823664259.png

导致原因:古早的bug

关于typeof判断的原理:typeof 通过将数值转化为二进制的形式来判断类型
关于引用类型:引用类型数据的二进制前三位都为0,所以二进制前三位都为0的,会被判断成Object类型
关于 nullnull转为二进制时,所有二进制位均为0,所以会被被typeof函数判断为Object类型

使用 instanceof 函数 判断类型

简单类型

都无法判断

afae1403e753c692f3e60c91a76289ce.png

引用类型

都可以判断

456ec486f98a45416f42cc0e92861ceb.png

实现原理

通过隐式原型链的查找来判断x是否隶属于X这个类型 例子:明明 "[ ]" 是Array类型,但是却能够被成功判断为Object类型

4df89edf28e1c6eec3d49ef492bd7ec5.png

原型链: instanceof 函数沿着 x对象的隐式原型链往上查找,直到找到X的显式原型

d9afcd1172d340508d25c095b1103fac~tplv-k3u1fbpfcp-zoom-in-crop-mark_1512_0_0_0.webp

手搓 instanceof 函数

function myInstanceof(obj, OBJ) {
    //因为 instanceof 函数无法判断原始类型,则自己打造的函数需要排除所有原始类型
    if ((typeof (obj) == Object || typeof (obj) == 'function') && obj != null) {
        while (obj.__proto__) { //沿着隐式原型链接向上查找
            if (obj.__proto__ == OBJ.prototype) {
                //函数
                return true;
            }
            obj = obj.__proto__;
        }
    }
    return false;
}

使用Object.prototype.toString.call() 判断对象类型

可以用于判断所有类型的数据

1764406281753_2D6B54A6-43FE-432f-A9F6-43C42ADD5A38.png

我们得到了 '[object Array]' 这一字符串,只要对其进行一定的处理便可以用于类型判断。

处理过程: 1764406616725_BE87953B-CE3D-4865-90E3-D20A4A0B881C.png

实现原理

官方原文

d94d77215a5636ed8b93ce5fcf4d3efe.png

翻译:

  1. 如果传入值为undefined,则返回字符串"[object Undefined]".
  2. 如果传入值为null,则返回字符串"[object Null]".
  3. 将this值传递给ToObject函数,设 O 为调用 ToObject 的结果, 即 const o = ToObject(this)
  4. 设一个变量 class 为 O 内部属性[[class]]的属性值. 即 const class = O.[[class]] (class 值为 创建该对象 的 构造函数名)
  5. 最后返回一个字符串,该字符串由三部分组成 ['[object'+ class +']']

Object.prototype.toString.call() 中 call()的作用

我们可以先试一下如果没有call()函数会得到一个什么情况
bd778e9616c579339d39f2fc407903e3.png

明明传入的是个字符类型,但是函数却判断的是Object类型。因为toString函数中的this指向的是Object.prototype 对象。

call()函数的意义

call可以将toString内部的this绑定到传入的对象上

2552200fb284ee6ea12e028e3815657a.png

使用Array.isArray( )判断对象是否是数组类型

如何使用 Array.isArray(obj)

c11fec180fa36fe3111b26187bbd71cf.png

实现原理与手搓一个Array.isArray( )

原理

利用 obj.proto == Array.prototype 判断obj是否属于Array类型

Array.myIsArray = function (obj) {
    if (obj) {
        if (obj.__proto__ == Array.prototype) {
            return true;
        }
    }
    return false
}
dca13dbba19c6f1e4c02eb23d9cd2603.png