JS百题斩~ typeof 、instanceof 与 Object.prototype.toString 区别(简单易懂)

首先,让我们先了解一下JavaScript的数据类型,分为两类:
基础类型:Undefined,Null,Boolean,Number,BigInt,String,Symbol
引用类型:Object,Array,Function

Symbol 是ES6中引入的一种原始数据类型,表示独一无二的值。BigInt(大整数)是 ES2020 引入的一种新的数据类型,用来解决 JavaScript中数字只能到 53 个二进制位。为了与 Number 类型区别,BigInt 类型的数据必须添加后缀n。 123为普通整数,123n为 BigInt。

typeof MDN docs

typeof是一个操作符而不是函数,用来检测给定变量的数据类型。typeof 总是返回一个字符串,用来说明变量的数据类型。
基本用法:

// 数值
typeof 37 === "number";
typeof NaN === "number";
typeof 42n === "bigint";

// 字符串
typeof "1" === "string"; 

// 布尔值
typeof true === "boolean";

// Symbols
typeof Symbol("foo") === "symbol";

// Undefined
typeof undefined === "undefined";

// Null
typeof null=== "object"; // JavaScript 诞生以来便如此

// 对象
typeof { a: 1 } === "object";
typeof [1, 2, 4] === "object";
typeof new Date() === "object";
typeof /regex/ === "object";

// 函数
typeof function () {} === "function";

上面可以看出,typeof 可以对除了null的基本数据类型做出准确的判断外,不能判断对象(如数组,正则等)具体是哪种类型,返回值都为“object”(除了Function会被识别出来),但是这些对象可以通过Object.prototype.toString.call() 查看内部属性。
在这里插入图片描述

typeof 操作符的优先级高于加法(+)等二进制操作符。因此,需要用括号来计算加法结果的类型。
// 括号有无将决定表达式的类型。
const num = 2;
typeof num + " foo"; // “number foo”
typeof (num+ " foo"); // “string”

typeof 原理

在JavaScript中,所有数值类型在底层都是以二进制形式表示的,由一个表示类型的标签和实际数据值表示的,前三位存储其类型信息。

二进制中的“前”一般代表低位, 比如二进制00000011对应十进制数是3,它的前三位是011。

  • 000: 对象
  • 010: 浮点数
  • 100:字符串
  • 110: 布尔
  • 1: 整数

因为对象的二进制表达前三位都是000,所以typeof无法区分。
由于 null 代表的是空指针(大多数平台下值为 0x00),因此null 的类型标签是 0,typeof null 也因此返回 “object”。

instanceof MDN docs

语法:object instanceof constructor
instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上(下篇讲原型链)。
通俗一些讲,instanceof 运算符用来检测一个对象是否为某一个构造函数的实例。注意,instanceof只能用于对象,不适用原始类型的值。

// 定义构造函数
function C() {}
function D() {}

var o = new C();

o instanceof C; // true,因为 Object.getPrototypeOf(o) === C.prototype

o instanceof D; // false,因为 D.prototype 不在 o 的原型链上

o instanceof Object; // true,因为 Object.prototype.isPrototypeOf(o) 返回 true
C.prototype instanceof Object; // true,同上

C.prototype = {};
var o2 = new C();

o2 instanceof C; // true

o instanceof C; // false,C.prototype 指向了一个空对象,这个空对象不在 o 的原型链上。

D.prototype = new C(); // 继承
var o3 = new D();
o3 instanceof D; // true
o3 instanceof C; // true 因为 C.prototype 现在在 o3 的原型链上

需要注意的是,如果表达式 obj instanceof Foo 返回 true,则并不意味着该表达式会永远返回 true,因为 Foo.prototype 属性的值有可能会改变,改变之后的值很有可能不存在于 obj 的原型链上,这时原表达式的值就会成为 false。另外一种情况下,原表达式的值也会改变,就是改变对象 obj 的原型链的情况,虽然在目前的 ES 规范中,我们只能读取对象的原型而不能改变它,但借助于非标准的 proto 伪属性,是可以实现的。比如执行 obj.proto = {} 之后,obj instanceof Foo 就会返回 false 了。

关于 instanceof 的实现原理,参考下面代码:

function myInstanceof(left, right) {
	// 这里先用 typeof 判断基础数据类型,如果是,直接返回false
	if (typeof left !== 'object' || left === null) return false;
	// getPrototypeOf是Object对象自带的API,能够拿到left参数的原型对象
	let proto = Object.getPrototypeOf(left);
	while(true) {
		if (proto === null) return false;
		if (proto === right.prototype) return true; // 找到相同原型对象,返回true
		proto = Object.getPrototypeOf(left);
	}
}

顺着原型链去找,直到找到相同的原型对象。

区别

typeof 与 instanceof 都是判断数据类型的方法,区别如下:

  • typeof 返回一个字符串,是变量的基本类型,instanceof 返回的是一个Boolean值;
  • instanceof 可以准确地判断复杂引用数据类型,但不能判断基础数据类型;
  • typeof 虽然可以判断基础数据类型( null 除外),但是引用数据类型中,除了Function类型以外,其他的也无法判断;

上述两种检测方法都有弊端,不能满足所有场景的需求,如果需要通用数据类型检测方法,可以使用Object.prototype.toString。

Object.prototype.toString.call() 推荐! MDN docs

这是对象的一个原生原型扩展函数,用来更精确的区分数据类型。
对于 Object.prototype.toString() 方法,调用该方法,返回统一的格式"[object xxxx]"的字符串。如果对象的 toString() 方法未被重写,就会返回如上面形式的字符串。
但是,大多数对象,toString() 方法都是重写了的,这时,需要用 call() 方法来调用。

Object.prototype.toString({});  // "[object Object]"
Object.prototype.toString.call({});  // "[object Object]"
Object.prototype.toString.call(1);  // "[object Number]"
Object.prototype.toString.call('1');  // "[object String]"
Object.prototype.toString.call(true);  // "[object Boolean]"
Object.prototype.toString.call(function(){});  // "[object Function]"
Object.prototype.toString.call(null);  // "[object Null]"
Object.prototype.toString.call(undefined);  // "[object Undefined]"
Object.prototype.toString.call(/123/g);  // "[object RegExp]"
Object.prototype.toString.call(new Date());  // "[object Date]"
Object.prototype.toString.call([]);  // "[object Array]"
Object.prototype.toString.call(document);  // "[object HTMLDocument]"
Object.prototype.toString.call(window);  // "[object Window]"

下一篇(待完成): JS百题斩~ 原型 与 原型链

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/679613.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

网络简史-基于图论的网络

先看一幅图: 如图,我们对类似 crossbar,banyan tree,b-tree,10-tree,256-tree,甚至 dcn fat-tree 等 “规则拓扑” 网络相当熟悉。规则拓扑网络中,地址信息被编码到拓扑本身&#…

每天复习一点小CTF知识(6.4)

NSSCTF/[FSCTF 2023]夜深人静的时候也会偷偷emo 直接爆破压缩包,先来数字 解压好,一个flag.mp3 mp3隐写,直接干 得一个txt文件直接开

2024最新破解版CorelDRAW解锁设计新境界!

在当今快速变化的市场环境中,品牌之间的竞争愈发激烈。为了在众多品牌中脱颖而出,企业需要不断地提升自身的品牌形象和市场识别度。而在这个过程中,视觉设计起到了至关重要的作用。一款优秀的设计软件不仅能帮助设计师轻松地将创意想法变成现…

【全网唯一】触摸精灵iOS版纯离线本地文字识别插件

目的 触摸精灵iOS是一款可以模拟鼠标和键盘操作的自动化工具。它可以帮助用户自动完成一些重复的、繁琐的任务,节省大量人工操作的时间。但触摸精灵的图色功能比较单一,无法识别屏幕上的图像,根据图像的变化自动执行相应的操作。本篇文章主要…

《平渊》· 捌 —— 「庄子」山木篇中的 “空船效应”

《平渊》 捌 "若能虚己以游世,其孰能害之?" "方舟而济于河,有虚船来触舟,虽有惼心之人而不怒;有一人子其上,则呼张歙之;一呼而不闻,再呼而不闻,于是三呼邪…

热烈祝贺2024年重庆市建筑电气学术年会圆满成功

芳菲四月花似锦,正是人间好时节,4月26日, 24年重庆市建筑电气学术年会在重庆维景国际大酒店举办。会议主题是“智慧 低碳 绿色 安全 ”。会上业内专家就建筑电气行业的新技术、市场趋势和未来发展展开探讨与深入交流。 本次会议邀请重庆市建筑电气行业主…

vue中实现一个时间选择器的级联框,第一层小时,第二层分钟

最近在做一个考勤系统时&#xff0c;新增班次的时候需要设置打卡时段&#xff0c;类似如下效果&#xff1a; 1、封装自定义组件Time.vue 接收参数有endHour(范围结束的小时数)、endMinute(最后一小时结束的分钟数)等&#xff0c;根据具体需求变动 <template><div&…

【代码+详解】算法题 : 最大公约数

❗❗❗必看: 下列题我全部都使用 Java 语言写的,并且均可以提交成功,获得Accepted 结果的. 如果代码和详解看了之后,对答案有任何疑问,都可以在评论区提出来,我都会一个一个回答. ❗❗❗感谢大家的支持,如果喜欢我的博客,关注 点赞 收藏 评论一波,非常感谢!!! 文章目录 题目&…

低代码和零代码软件时代质量管理(QM)和质量管理系统(QMS)

【前言】 质量控制过程的目的是为了确保产品的制造标准得到保持和改进。质量控制过程使公司能够满足客户的期望&#xff0c;同时确保产品质量的一致水平。采用这些标准创造了一种公司文化&#xff0c;鼓励所有员工努力实现高质量的生产标准。低代码和零代码软件可以成为质量控…

6.4学习总结

Codeforces Round 950 (Div. 3)A、B题解 解题思路 开一个数组来记录A,B,C,D,E,F,G难度题目出现的次数&#xff0c;因为每一轮比赛都需要每一种难度都有一题&#xff0c;所以我们只要根据要出的比赛的轮数对每一个难度的题目进行自减&#xff0c;最后遍历数组把所有为负数的题目…

vscode编译文件夹下所有文件的配置(包含插件和 .json 文件)

文章目录 我所使用的插件.json 文件配置1. c_cpp_properties.json2. launch.json3. settings.json4. tasks.json 如何运行 我所使用的插件 红框中的五个插件是必备的&#xff0c;其中 Code Runner 插件可以在写完一个 .c 或 .cpp 文件后&#xff0c;按下 Crtl R 快捷键快速编…

12-学生们参加各科测试的次数(高频 SQL 50 题基础版)

12-学生们参加各科测试的次数 -- 学生表中&#xff0c;id是唯一的&#xff0c;将他作为主表 -- CROSS JOIN产生了一个结果集&#xff0c;该结果集是两个关联表的行的乘积 -- 2行表,与3行表使用cross join,得到2*36行数据 select st.student_id, st.student_name,su.subject_na…

zdppy_api 中间件请求原理详解

单个中间件的逻辑 整体执行流程&#xff1a; 1、客户端发起请求2、中间件拦截请求&#xff0c;在请求开始之前执行业务逻辑3、API服务接收到中间件处理之后的请求&#xff0c;和数据库交互&#xff0c;请求数据4、数据库返回数据5、API处理数据库的数据&#xff0c;然后给客户…

计算机基础(3)——计算机系统组成

&#x1f497;计算机基础系列文章&#x1f497; &#x1f449;&#x1f340;计算机基础&#xff08;1&#xff09;——计算机的发展史&#x1f340;&#x1f449;&#x1f340;计算机基础&#xff08;2&#xff09;——冯诺依曼体系结构&#x1f340;&#x1f449;&#x1f34…

透视亚马逊云科技中国峰会:生成式AI全面提速,加速行业应用落地

导读&#xff1a;亚马逊云科技在中国&#xff0c;生成式AI与行业化战略齐头并进。 “亚马逊云科技致力于成为企业构建和应用生成式AI的首选。” 近日2024亚马逊云科技中国峰会上&#xff0c;亚马逊全球副总裁、亚马逊云科技大中华区总裁储瑞松分享了亚马逊云科技中国业务最新进…

嵌入式Linux系统编程 — 1.4 原子操作与竞争冒险

目录 1 竞争冒险 1.1 竞争冒险由来 1.2 竞争冒险理解 2 原子操作 2.1 O_APPEND 实现原子操作 2.2 pread()和 pwrite() 2.3 O_EXCL 标志创建文件 1 竞争冒险 1.1 竞争冒险由来 Linux 是一个支持多任务和多用户同时运行的操作系统&#xff0c;它允许多个进程同时执行。…

京东笔试-校招

2022京东数据分析笔试&#xff08;0821&#xff09; 一、选择题&#xff1a;30道 1.解决数据不平衡的方法主要有&#xff08;pca&#xff1f;&#xff09; 2.等频&#xff08;等宽&#xff09;划分问题 3.参数估计&#xff1a;矩估计与极大似然估计的用法&#xff0c;问题分…

kill 不管用时,类型为C

当使用nvidia-smi时看到类型为C的进程时&#xff0c;使用 kill -9 PID&#xff0c;却不管用&#xff0c;这时需要先使用如下命令&#xff0c;找出运行的脚本对应的所有PID: ps -aux | grep train.py 接着就会把train.py对应运行的进程全部展示出来&#xff1a; 接着就是使用 …

25、DHCP FTP

DHCP 动态主机配置协议 DHCP定义&#xff1a; 服务器配置好了地址池 192.168.233.10 192.168.233.20 客户端从地址池当中随机获取一个ip地址&#xff0c;ip地址会发生变化&#xff0c;使用服务端提供的ip地址&#xff0c;时间限制&#xff0c;重启之后也会更换。 DHCP优点&a…

android-JNI

1.2【静态库】的特点&#xff1a; &#xff08;.a&#xff09; ①静态库对函数库的链接是在编译期完成的。执行期间代码装载速度快。 ②使可执行文件变大&#xff0c;浪费空间和资源&#xff08;占空间&#xff09;。 ③对程序的更新、部署与发布不方便&#xff0c;需要全量更新…