三种“类型判断”的方法,一起手写instanceof方法的实现原理,面试官让你手写不再害怕

在JavaScript中,有三种常用的方法用于判断数据类型:

1. typeof操作符

typeof操作符是最常用的判断数据类型的方法之一。它是一个一元操作符,可以用于判断一个值的数据类型,并返回一个表示该数据类型的字符串。常见的typeof返回值有:

  • "undefined": 用于表示未定义的变量

  • "number": 用于表示数字类型

  • "string": 用于表示字符串类型

  • "boolean": 用于表示布尔类型

  • "object": 用于表示对象类型(包括数组、日期、正则表达式等)

  • "function": 用于表示函数类型

  • "bigint": 用于表示大数字类型

  • "symbol": 用于保证创建的值不与其他属性名产生冲突

例如:

javascript 复制代码
typeof undefined; // "undefined"  
typeof 42; // "number"  
typeof "hello"; // "string"  
typeof true; // "boolean"  
typeof { name: "John" }; // "object"  
typeof function() {}; // "function"  
typeof 123n; // "bigint"
typeof symbol('hello'); //"symbol"

需要注意的是,typeof对于null的判断返回的是"object",这是因为在JavaScript的早期版本中,null被错误地认为是一个对象。而对于函数类型,则返回"function".

2. instanceof操作符

instanceof操作符用于判断一个对象是否属于某个构造函数的实例。它比typeof更适用于判断对象类型,因为它可以准确地判断多层原型链中的实例关系。例如:

javascript 复制代码
var arr = [];  
var date = new Date();  
var obj = {};
var fn = function(){};
  
arr instanceof Array; // true  
date instanceof Date; // true  
obj instanceof Object; // true  
fn instanceof Function; // true  

instanceof判断的结果是一个布尔值,如果对象是指定构造函数的实例,则返回true,否则返回false。

同时,也正是因为instanceof操作符可以判断多层原型链中的实例关系,那函数和数组不也可以看作是一个对象吗,那用他们用instanceof操作符来判断和Object的关系,是不是也能得到true呢,我们来看一看:

javascript 复制代码
var arr = []; 
var fn = function(){};

arr instanceof Object; // true  
fn instanceof Object; // true  

结果也是true,欸,arr instanceof Array是true,arr instanceof Object也是true,这时候我们是不是开始好奇instanceof 方法的实现原理了,怎么这两个结果都能是true呢。

这时候我们不妨先来大胆猜测一波,我们都知道,arr数组的创建实际是通过new构造函数Array()得到的,那arr就是构造函数Array()的一个实例对象,所以当我们判断arr instanceof Array时, 如果在instanceof方法实现原理内部,用arr的隐式原型(arr.__proto__)去和Array的显示原型(Array.prototype)比较,相等就返回true,否则返回false,通过原型的知识,这么想arr instanceof Array返回true是不是非常合情合理。(注:如果还有不太懂原型知识的小伙伴可以先看看我之前的原型知识文章喔~

但是,又好像还差一点吼,那用刚刚那个想法判断arr instanceof Object,这时候就要用arr的隐式原型(arr.__proto__)去和Object的显示原型(Object.prototype)进行比较了,那他两是不是很明显就不相等了,这时估计有看过我原型文章聪明的小伙伴知道了,这时候他们不相等,arr的隐式原型arr.__proto__对象会再用它的隐式原型,也就是arr.__proto__.__proto__,再去和Object的显示原型(Object.prototype)进行比较,这时候会发现他们是不是就相等啦,结果返回true。

欸,对喽!就是这样思路!非常的棒!实例对象 instanceof 数据类型 就是先通过判断实例对象.__proto和数据类型的prototype是否相等,相等直接返回true,不相等就通过原型链,再往上找,看实例对象.__proto.__proto和数据类型的prototype是否相等,要是还不相等就再往上,直到实例对象.__proto.__proto.__proto为null了,还是和数据类型的prototype不相等,则返回false。

那我们带着这个思路是不是非常清晰的就知道了instanceof方法的实现原理啦!接下来,我们一起手写代码来实现一下叭!代码如下:

javascript 复制代码
//L 实例对象
//R 要判断的数据类型
function instanceOF(L,R){
    while(L !== null){
        // 用实例对象的__proto__属性和要判断的数据类型的prototype进行判断
        //相等返回true,不相等再用实例对象的__proto__的__proto__属性去判断
        //直到L.__proto__.__proto__...为null,就通过原型链根本找不到相等的了,返回false.
        if(L.__proto__ === R.prototype){  
            return true
        }
        else L = L.__proto__
    }
    return false
}

//验证我们手写的instanceOF是否正确
console.log(instanceOF([],Array)); //true
console.log(instanceOF([],Object));//true

所以,搞明白之后,是不是觉得手写instanceof 方法非常简单啦!好,那既然数组,函数用instanceof方法和object数据类型进行判断都为true,所以这种方法好像也不是那么完美,并不能精准判断对象的类型。那我们不妨看看下面另一种方法。

3. Object.prototype.toString方法

Object.prototype.toString是一个通用的方法,可以返回一个对象的内部属性[[Class]]的值,从而判断对象的类型。toString方法被重写,并通过不同的内部属性来标识不同的类型。例如:

javascript 复制代码
Object.prototype.toString.call(undefined); // "[object Undefined]"  
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({ name: "John" }); // "[object Object]"  
Object.prototype.toString.call(function() {}); // "[object Function]"  

这时小伙伴要问啦,不是讲Object.prototype.toString方法嘛,怎么后面还跟了一个call方法呢,好,这时候我们试试不加call方法,看看结果如何:

javascript 复制代码
console.log(Object.prototype.toString(undefined))// "[object Object]"
console.log(Object.prototype.toString(42))// "[object Object]"  
console.log(Object.prototype.toString("hello"))// "[object Object]" 

这时会发现结果都是[object Object]对象类型,当直接调Object.prototype.toString时,它的 this 值会被设置为 toString 方法的调用者,也就是要检查类型的对象本身,这里就是Object.prototype对象,所以得到的结果都是对象类型。这时可以通过call方法来改变它的调用者,从而将this值设置为我们想要检查类型的对象。这样做可以确保我们获取到了准确的内部属性 [[Class]] 的信息,用于判断对象的类型。所以,使用Object.prototype.toString.call方法可以准确地检查对象的类型,而不受调用者的影响。这是一种常用的方法来判断对象类型的标准做法。

那call方法是如何做到将this值设置为我们想要检查类型的对象呢?欢迎各位小伙伴关注我的下一篇文章,将会详细讲解call方法的实现原理,以及将通过官方文档和小伙伴们深入讲解Object.prototype.toString方法,各位小伙伴可以先关注一下小博主喔~❤️❤️❤️

总结

需要注意的是,这三种判断类型的方法在某些场景下可能会有限制或产生歧义,因为JavaScript中的数据类型特点复杂多样。所以,在实际使用中,要结合具体场景和需求,选择合适的方法进行数据类型的判断。

如果觉得文章对您有所帮助的话,麻烦给小博主点点关注,点点赞咯😘,有问题欢迎各位小伙伴评论喔😊~

相关推荐
我要洋人死几秒前
导航栏及下拉菜单的实现
前端·css·css3
川石课堂软件测试3 分钟前
性能测试|docker容器下搭建JMeter+Grafana+Influxdb监控可视化平台
运维·javascript·深度学习·jmeter·docker·容器·grafana
科技探秘人12 分钟前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人12 分钟前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR18 分钟前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香19 分钟前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q24985969322 分钟前
前端预览word、excel、ppt
前端·word·excel
小华同学ai28 分钟前
wflow-web:开源啦 ,高仿钉钉、飞书、企业微信的审批流程设计器,轻松打造属于你的工作流设计器
前端·钉钉·飞书
problc32 分钟前
Flutter中文字体设置指南:打造个性化的应用体验
android·javascript·flutter
Gavin_91536 分钟前
【JavaScript】模块化开发
前端·javascript·vue.js