一文搞懂==中的隐式类型转换🔥🔥

前言

众所周知javascript是一种弱类型语言。强类型和弱类型主要是站在变量类型处理的角度进行分类的。强类型是一旦指定数据类型,如果不经过强制转换,那么将永远是指定的这个类型。js中无法声明数据类型,变量类型是根据实际值决定的,由编译器自动调用转换函数进行转换,这种方式称之为隐式转换,再js中能造成隐式转换的操作符有很多,比如==, +, *, /, -等,今天主要详细讲解==中的隐式类型转换。

js中的数据类型

基础数据类型

  1. string字符串类型
  2. number数字类型
  3. undefined类型
  4. null类型
  5. boolean布尔类型
  6. symbol类型(es6)
  7. bigint类型(es6)

引用数据类型

  1. object类型
  2. array类型
  3. function类型

==中的隐式类型转换

先上结论图:

  1. null与undefined类型在==中进行比较时,这两种类型一直都返回true
ini 复制代码
null == undefined // true
null == null // true
undefined == undefined // true

但是当这两种类型中的任意类型与其它类型==比较时都会返回false

ini 复制代码
null == 123 // false
null == {} // false
null == [] // false
null == '' // false

2.当==两边类型相同时,如果两边的值都是基础类型时直接比较他们的原始值是否相同,如果两边的值都是引用类型时直接比较他们的引用地址是否相同.

ini 复制代码
'aaa' == 'aaa' // true
'aaa' == 'bbb' // false

const obj = {};
obj == {} // false 引用地址不同
obj == obj // true

3.当==两边的类型不同时,就需要js进行隐式类型转换。如果两边都是基础数据类型 ,则会调用ToNumber(input)将值转换为数字 进行比较。如果一边是基础数据类型一边是引用数据类型,引用数据类型则会调用ToPrimitive()将引用类型值转化为原始值,转化为原始类型值后借助上述规则进行比较。

看到这里的小伙伴肯定又有疑惑了,什么是ToNumber,什么又是ToPrimitive?下面对这两个抽象操作进行讲解。

ToPrimitive(input, type?)

ToPrimitice是JavaScript引擎内部的一个抽象操作,将参数input转换成原始值,如果传入的参数原本就是初始值则直接返回。否则会根据type的不同有不同的表现。

type值为number

  1. 调用对象身上的valueOf方法,如果该方法返回基础数据类型,则返回该基础类型值。
  2. 如果valueOf方法返回不是基础数据类型,则调用对象身上的toString方法,如果返回基础数据类型值则返回。
  3. 如果toString方法也没有返回基础数据类型值,则抛出TypeError异常.

type值为string

  1. 调用对象身上的toString方法,如果该方法返回基础数据类型,则返回该基础类型值。
  2. 如果toString方法返回不是基础数据类型,则调用对象身上的valueOf方法,如果返回基础数据类型值则返回。
  3. 如果valueOf方法也没有返回基础数据类型值,则抛出TypeError异常.

不传递type参数

不传递type参数时,对于Date日期对象,则相当于type为string的情况,而其他对象相当于type为number的情况。

再==比较时,对象调用ToPrimitive函数进行转化时就属于不传递type参数的情况。

那valueOf()和toString()又是什么呢?接下来再来看看这两个函数:

valueOf()

返回对象的原始值表示。valueOf是Object.prototype的方法,由Object来的对象都会有该方法,但是很多内置对象会重写这个方法,以适合实际需要。它的转化规则如下:

对象 转化后的值
String对象 对应的字符串值
Number对象 对应的数字值
Boolean对象 对应的布尔值值
Object对象({}) 对象本身
数组对象 数组本身
函数对象 函数本身
Date日期对象 对应的时间戳毫秒值

toString()

返回对象的字符串表示。toString是Object.prototype的方法,由Object来的对象都会有该方法,但是很多内置对象会重写这个方法,以适合实际需要。它的转化规则如下:

对象 转化后的值
String对象 对应的字符串值
Number对象 '数字'
Boolean对象 '布尔值'
Object对象({}) "[object Object]"
数组对象 数组中每项用逗号分割的字符串,空数组会转化为空字符串
函数对象 函数体字符串
Date日期对象 可读的时间字符串

ToNumber()

ToNumber()其实就是将值转化为数字,其实就是相当于调用Number()函数。它的转化规则如下:

类型 转化后的结果
Undefined NaN
Null +0
Boolean argument 为 true, return 1; argument 为 false, return 0
Number return argument参数
String 将字符串中的内容转化为数字(比如"23"->23)空字符串转化为数字0,如果转化失败则返回NaN(比如"23a"->NaN)
Symbol 抛出 TypeError 异常
Object 先primValue = ToPrimitive(input, 'number'),再对primValue使用ToNumber(primValue)

接下来就看几个例子

==两边类型不同(两边都为基础类型)

ini 复制代码
'123' == 123 // true,字符串调用ToNumber转化为数字后为123
'a123' == 123 // false,字符串调用ToNumber转化为数字后为NaN
true == 1 // true, 布尔值true转化为数字为1
false == 0 // true,布尔值true转化为数字为0
true == '1' // true,布尔值true转化为数字为1,字符串调用ToNumber转化为数字后为1
false == '' // true,布尔值true转化为数字为0,空字符串转化为数字0

==两边类型不同(一边基础类型,一边引用类型),为了观察引用类型调用ToPrimitive是否如我们上边所说的,要先对Object.prototype上valueOf和toString方法进行重写

javascript 复制代码
// 保存原始的valueOf
var valueOf = Object.prototype.valueOf;
var toString = Object.prototype.toString;

// 添加valueOf日志 
Object.prototype.valueOf = function () { 
  console.log('valueOf'); 
  return valueOf.call(this); 
}; 
// 添加toString日志 
Object.prototype.toString = function () { 
  console.log('toString'); 
  return toString.call(this); 
};

const obj = {};
obj == '123' // false
// {}由valueOf和toString转化后为[object Object]的字符串,
// 而等号右边也是字符串,相同类型直接比较值。

obj == 123 // false
// {}由valueOf和toString转化后为[object Object]的字符串,而等号右边是数字,
// 又转化为不同类型比较,
// 那么就会将不属于数字类型的值转化为数字类型[object Object]转化后为NaN,所以是false。

obj == '[object Object]' // true

从控制台打印可以看出再进行比较时确实是先调用了valueOf再调用toString,接下来再看看数组对象 的比较过程:同理先将Array.prototype上的valueOf和toString方法进行重写这里就不展示了.

sql 复制代码
[] == 0 // true
// []先调用valueOf返回数组本身,再调用toString转化为'',之后就是''==0的比较流程
//''转化为数字0

[] == '' // true
// []先调用valueOf返回数组本身,再调用toString转化为'',之后就是''==''比较流程

[1] == 1 // true
// []先调用valueOf返回数组本身,再调用toString转化为'1',之后就是'1'==1的比较流程
//'1'转化为数字1

[1, 2] == '1,2' // true
// []先调用valueOf返回数组本身,再调用toString转化为'1,2',之后就是'1,2'=='1,2'的比较流程

[null] == 0 // true
// []先调用valueOf返回数组本身,再调用toString转化为'',之后就是''==0的比较流程
//''转化为数字0

[null, undefined] == ',' // true
// []先调用valueOf返回数组本身,再调用toString转化为',',之后就是','==','的比较流程

[] == false // true
// []先调用valueOf返回数组本身,再调用toString转化为'',之后就是''==false的比较流程
//''转化为数字0,false转化为0

[0] == false // true
// []先调用valueOf返回数组本身,再调用toString转化为'0',之后就是'0'==false的比较流程
//'0'转化为数字0,false转化为数字0

[1] == true // true

Date对象再==比较中也是先调用ToPrimitive()进行转化,不过Date对象相当于是ToPrimitive(input, 'string'),也就是先调用toString再根据情况调用valueOf.

javascript 复制代码
// 添加valueOf日志 
Date.prototype.valueOf = function () { 
  console.log('valueOf'); 
  return 'bbb';
}; 
// 添加toString日志 
Date.prototype.toString = function () { 
  console.log('toString'); 
  return 'aaa'; 
};

new Date() == 'aaa' // true
// 先调用toString返回'aaa'是一个基础数据类型值,则不再去调用valueOf

// 添加valueOf日志 
Date.prototype.valueOf = function () { 
  console.log('valueOf'); 
  return 'bbb';
}; 
// 添加toString日志 
Date.prototype.toString = function () { 
  console.log('toString'); 
  return {}; 
};

new Date() == 'bbb' // true;
// 先调用toString返回{}不是一个基础数据类型值,则继续去调用valueOf,返回'bbb'。

面试题

1.如何使a == 1 && a == 2 && a == 3成立?

css 复制代码
const a = {
  count: 1,
  valueOf() {
    return a.count++
  }
}
  1. \] == !\[

该式子比较结果为true,因为![]为false,那么转化为[]==false,而[]转化为初始值为'' 也就是''==false,再将两边同时转化为数字0==0为true. 3. [null] == ![]

该式子也为true,因为[null]转化为初始类型值也是'',也就是''==false,再将两边同时转化为数字0==0为true.

如有错误,欢迎指出!

相关推荐
西哥写代码21 分钟前
基于cornerstone3D的dicom影像浏览器 第十八章 自定义序列自动播放条
前端·javascript·vue
独行soc22 分钟前
2025年渗透测试面试题总结-百度面经(题目+回答)
运维·开发语言·经验分享·学习·面试·渗透测试·php
清风细雨_林木木25 分钟前
Vue 中生成源码映射文件,配置 map
前端·javascript·vue.js
雪芽蓝域zzs1 小时前
JavaScript splice() 方法
开发语言·javascript·ecmascript
编程、小哥哥2 小时前
Java大厂面试:从Web框架到微服务技术的场景化提问与解析
java·spring boot·微服务·面试·技术栈·数据库设计·分布式系统
森叶2 小时前
Electron 主进程中使用Worker来创建不同间隔的定时器实现过程
前端·javascript·electron
霸王蟹2 小时前
React 19 中的useRef得到了进一步加强。
前端·javascript·笔记·学习·react.js·ts
霸王蟹2 小时前
React 19版本refs也支持清理函数了。
前端·javascript·笔记·react.js·前端框架·ts
codelxy2 小时前
vue引用cesium,解决“Not allowed to load local resource”报错
javascript·vue.js
程序猿阿伟3 小时前
《社交应用动态表情:RN与Flutter实战解码》
javascript·flutter·react native