前端必刷八股:typeof/instanceof的灵魂拷问

172 阅读4分钟

前言

作者最近在看神三元大佬的JS灵魂之问系列和程序员鱼皮的面试鸭的八股系列,再结合一下《你不知道的JavaScript》,准备借这篇文章对JS的几道常见的八股题进行总结。其中加入了很多自己的思考,大家也可以参考这篇文章进行复习一下这几道JS的八股。

一、Instanceof 和 typeof的区别是什么

Instanceof 和 typeof 都可以用来判断数据类型是什么,它们的区别如下:

应用场景的区别:

Instanceof 通常是用来判断 引用数据类型(对象)的具体类型,比如判断:

var arr = [123];
console.log(arr instanceof Array) //true

typeof 通常是用来判断 简单数据类型的具体类型,比如判断:

var str = "Im str";
console.log(typeof str) //string

原理的区别:

Instanceof 的原理

A instanceof B会查找A的原型链 查看是否存在B.prototype,如果存在的话就会返回true,如果直至找到最顶部仍然找不到,则返回false

而对于简单数据类型来说(number,string,boolean...)是没有原型链的,所以简单类型使用instanceof时,始终会返回false,因此instanceof不适合简单数据类型的具体判断。

typeof 的原理

typeof的使用方式是typeof 变量,他对变量类型的判断原理如下:

  1. 对 简单类型,直接检查值的二进制类型标记,(比如浮点数二进制后的低位类型标记为010)
  2. 对 引用类型,检查对象是否可调用(函数返回 "function",其他返回 "object")。
  3. null 因历史遗留问题被错误归类为 "object"

拓展:Object.prototype.toString.call()

如果想要准确地识别所有的数据类型,可以用Object.prototype.toString.call() ,它是js中最可靠的数据类型判断方法

对于简单数据类型(Primitive Types):
Object.prototype.toString.call()可以将简单数据类型自动转换成对应的包装数据类型

// 简单类型(Primitive Types)
Object.prototype.toString.call(42);          // "[object Number]"
Object.prototype.toString.call("hello");     // "[object String]"
Object.prototype.toString.call(true);        // "[object Boolean]"
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(42n);         // "[object BigInt]"

对于引用数据类型(Objct Types):
Object.prototype.toString.call()可以直接判断其对应的类型

// 引用类型(Object Types)
Object.prototype.toString.call({});          // "[object Object]"
Object.prototype.toString.call([]);          // "[object Array]"
Object.prototype.toString.call(new Date());   // "[object Date]"
Object.prototype.toString.call(/regex/);     // "[object RegExp]"
Object.prototype.toString.call(() => {});    // "[object Function]"
Object.prototype.toString.call(new Map());    // "[object Map]"
Object.prototype.toString.call(new Set());    // "[object Set]"
Object.prototype.toString.call(new Error()); // "[object Error]"
Object.prototype.toString.call(window);      // "[object Window]"(浏览器环境)
Object.prototype.toString.call(Math);        // "[object Math]"
Object.prototype.toString.call(JSON);        // "[object JSON]"


二、为什么typeof null的结果为object?

因为js在早期使用32位方式来存储数据类型,其中前3位表示用作类型标签(比如010表示浮点数)

对于Object,它在转换为二进制后的低位(后三位)为000,而恰好null在转化为二进制后的全为0,因此它的低位也为000

typeof 变量在判断时会基于转换后的二进制,所以就导致了typeof null会被JS引擎误判为object

这是一个历史遗留问题,由于后期修复它可能会影响Js的其它部分,所以到现在这个现象仍然存在。

拓展:如何正确判断null

如果想要正确判断null的话:可以使用以下的方法

// 方法1:严格等于
value === null;

// 方法2:Object.prototype.toString(最可靠)
Object.prototype.toString.call(null); // "[object Null]"

// 方法3:组合判断(兼容旧代码)
function isNull(value) {
  return value === null || Object.prototype.toString.call(value) === "[object Null]";
}



三、能否手动实现instanceof的功能

手写instanceof的代码如下

function myInstanceof(left, right) {
    //基本数据类型直接返回false
    if(typeof left !== 'object' || left === null) return false;
    //getProtypeOf是Object对象自带的一个方法,能够拿到参数的原型对象
    let proto = Object.getPrototypeOf(left);
    while(true) {
        //查找到尽头,还没找到
        if(proto == null) return false;
        //找到相同的原型对象
        if(proto == right.prototype) return true;
        proto = Object.getPrototypeOf(proto);
    }
}  

拓展:instanceof能否判断简单数据类型

可以,但是需要我们手写重新实现instance

class PrimitiveNumber {
  static [Symbol.hasInstance](x) {
    return typeof x === 'number'
  }
}
console.log(111 instanceof PrimitiveNumber) // true

这里手写了instance判断 该数据类型是否为number,其中最主要的是static [Symbol.hasInstance](x) { return typeof x === 'number' }

Symbol.hasInstance 是一个JS内置的 Symbol 值,用于自定义 instanceof 的检查逻辑。当一个构造函数(或 class)定义了 [Symbol.hasInstance] 方法时,instanceof 会调用该方法来判断实例关系。


总结

本文分享的三道八股,可以帮助第一次刷到的人,学习一下对应的知识点,之前刷过的或者接触过的大佬,可以借此复习一下知识点,如果对文章的内容有一些意见以及异议的话,欢迎在评论区指出,创作不易,如果对你有帮助的话,可以点个赞呀!!