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

本文主要是介绍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.prototypeo 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; // trueo 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 判断基础数据类型,如果是,直接返回falseif (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; // 找到相同原型对象,返回trueproto = 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百题斩~ 原型 与 原型链

这篇关于JS百题斩~ typeof 、instanceof 与 Object.prototype.toString 区别(简单易懂)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1031181

相关文章

Java中的String.valueOf()和toString()方法区别小结

《Java中的String.valueOf()和toString()方法区别小结》字符串操作是开发者日常编程任务中不可或缺的一部分,转换为字符串是一种常见需求,其中最常见的就是String.value... 目录String.valueOf()方法方法定义方法实现使用示例使用场景toString()方法方法

用js控制视频播放进度基本示例代码

《用js控制视频播放进度基本示例代码》写前端的时候,很多的时候是需要支持要网页视频播放的功能,下面这篇文章主要给大家介绍了关于用js控制视频播放进度的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言html部分:JavaScript部分:注意:总结前言在javascript中控制视频播放

Mysql表的简单操作(基本技能)

《Mysql表的简单操作(基本技能)》在数据库中,表的操作主要包括表的创建、查看、修改、删除等,了解如何操作这些表是数据库管理和开发的基本技能,本文给大家介绍Mysql表的简单操作,感兴趣的朋友一起看... 目录3.1 创建表 3.2 查看表结构3.3 修改表3.4 实践案例:修改表在数据库中,表的操作主要

分辨率三兄弟LPI、DPI 和 PPI有什么区别? 搞清分辨率的那些事儿

《分辨率三兄弟LPI、DPI和PPI有什么区别?搞清分辨率的那些事儿》分辨率这个东西,真的是让人又爱又恨,为了搞清楚它,我可是翻阅了不少资料,最后发现“小7的背包”的解释最让我茅塞顿开,于是,我... 在谈到分辨率时,我们经常会遇到三个相似的缩写:PPI、DPI 和 LPI。虽然它们看起来差不多,但实际应用

springboot简单集成Security配置的教程

《springboot简单集成Security配置的教程》:本文主要介绍springboot简单集成Security配置的教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录集成Security安全框架引入依赖编写配置类WebSecurityConfig(自定义资源权限规则

GORM中Model和Table的区别及使用

《GORM中Model和Table的区别及使用》Model和Table是两种与数据库表交互的核心方法,但它们的用途和行为存在著差异,本文主要介绍了GORM中Model和Table的区别及使用,具有一... 目录1. Model 的作用与特点1.1 核心用途1.2 行为特点1.3 示例China编程代码2. Tab

Nginx指令add_header和proxy_set_header的区别及说明

《Nginx指令add_header和proxy_set_header的区别及说明》:本文主要介绍Nginx指令add_header和proxy_set_header的区别及说明,具有很好的参考价... 目录Nginx指令add_header和proxy_set_header区别如何理解反向代理?proxy

Java中&和&&以及|和||的区别、应用场景和代码示例

《Java中&和&&以及|和||的区别、应用场景和代码示例》:本文主要介绍Java中的逻辑运算符&、&&、|和||的区别,包括它们在布尔和整数类型上的应用,文中通过代码介绍的非常详细,需要的朋友可... 目录前言1. & 和 &&代码示例2. | 和 ||代码示例3. 为什么要使用 & 和 | 而不是总是使

如何使用Python实现一个简单的window任务管理器

《如何使用Python实现一个简单的window任务管理器》这篇文章主要为大家详细介绍了如何使用Python实现一个简单的window任务管理器,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起... 任务管理器效果图完整代码import tkinter as tkfrom tkinter i

C++中函数模板与类模板的简单使用及区别介绍

《C++中函数模板与类模板的简单使用及区别介绍》这篇文章介绍了C++中的模板机制,包括函数模板和类模板的概念、语法和实际应用,函数模板通过类型参数实现泛型操作,而类模板允许创建可处理多种数据类型的类,... 目录一、函数模板定义语法真实示例二、类模板三、关键区别四、注意事项 ‌在C++中,模板是实现泛型编程