js内部类型转换与‘==‘执行流程

最近没事就捣鼓浏览器控制台,发现了一个奇怪的现象,因此引发了一些思考。

[] == 0		// true 
{} == 0		// Uncaught SyntaxError: Unexpected token '=='
({}) == 0	//false
undefined == 0		// false
'' == 0		// true
null == 0		// false
false == 0		// true
new Number(0) == 0		// true
NaN == 0      //false

我们可以发现一个现象,那就是js内部,我们通常认为是空的东西,他们和0作’=='比较,有些为true,有些为false。众所周知, ’ == '的左右两边值会进行类型转换。那么它是一个什么流程呢?
本文引用《js红宝书》P71与《js权威指南(第七版)》P44~P51

‘==’ '!='转换规则

首先介绍’ == ’ 转换规则(!=与==规则一样)
ECMAScript在执行性’ == '操作时,如果操作数相等,则会返回true。如若两操作符不相等,则会根据如下规则进行强制类型转换:

  • 如果任一操作数是布尔值,则将其转化为数值再比较是否相等。false转化为0,true转化为1.

  • 如果一个操作数是字符串,另一个操作数是数值,则会尝试将字符串转化为数值,再比较是否相等。

  • 如果一个操作数是对象,另一个不是,则使用隐式类型无偏号转换原则(见后文)获取其原始值,再根据前面的规则进行比较。
    在比较时,有如下特殊情况,那就是存在undefined与null的情况:

  • null和undefined相等

  • null和undefined不能转化为其他类型的值再进行比较。(那就是如果一边存在null和undefined,另一边若不是null或undefined,直接返回false)

  • 若有任一操作数是NaN,则直接返回false。(另外,NaN是Number类型,但是是所有数据类型中唯一与自身不全等的数。在Object.is(NaN,NaN)中才会返回true。)

  • 若两个操作数都是对象,则比较他们指向的是不是同一个对象。如果两个操作数都指向同一个对象,则会返回true,否则返回false。(因此深浅拷贝出来的对象在’=='情况下也不相等。)
    下面总结了一些特殊情况下的比较结果:

表达式结果
null == undefinedtrue
‘NaN’ == NaNfalse
5 == NaNfalse
NaN == NaNfalse
NaN != NaNtrue
false == 0true
true == 1true
true == 2false
undefined == 0false
null == 0false
‘5’ == 5true
上面介绍了’=='的执行流程,下面介绍一下数据类型间的相互转换:

类型转换

我们还是先来通过几个例子来看一下:

10 + "object"		// "10object"
"7" * "4"			// 28
let n = 1 - "x"		// NaN
n + "objects"		// "NaNobjects"

js内部操作符隐式类型转换

在js中,某些操作符会执行隐式类型转换(后文介绍隐式类型转换规则),例如’+'操作符,如果该操作符作用于两个操作数,也就是a+b的方式,若其中存在一个操作数为String类型,会默认将另一个操作数也转化为String类型,但若该操作符只作用于一个操作数,如+a的情况,会将该操作数转化为Number类型。下面简单介绍几个该类操作符:

操作符表达式效果
+x + “”String(x)
++xNumber(x)
-x - 0Number(x)
*x * “1”Number(x)
!!!!xBoolean(x)

js显式类型转换

日常在工作需求中,我们也可能会碰到需要进行类型转换的时候,这里我们可以采用显式类型转化:
最常用的显式类型转换方法有Number(),String(),Boolean()。这三个方法的作用想必一看就懂,就不多做介绍。当然需要提到的是:除undefined与null外所有值都有toString()方法(为避免歧义,部分在调用时需要加上(),后文详细介绍各个类上重写的toString()方法。),这与String()显式转换后得到的值一样。另外就是将这些转换方法作为构造函数时,会得到一个对象类型的值(并不是构造函数的类型)。

转换到布尔值规则

  • null
  • undefined
  • “”
  • NaN +0 -0
  • false
    为false外,其他均为真。

转换到数值规则

  • Undefined 类型的值转换为 NaN
  • Null类型转化值为0
  • Boolean类型true为1,false为0
  • String类型若只含数字则直接转化并保留有效数字,(利用parseInt(进行显式转化时,若选择进制,则字母等也会被认为是数值))若含非数字字符串(除’+‘,’-'外),返回NaN.
  • Symbol类型不能转换为数字

转换到字符串规则

  • Null 和 Undefined 类型 ,null 转换为 “null”,undefined 转换为 “undefined”
  • Boolean 类型,true 转换为 “true”,false 转换为 “false”
  • Number 类型的值直接转换,不过那些极小和极大的数字会使用指数形式。(这个转换若调用toString()转换还可以选择进制转换,2~36进制均可选择。)
  • Symbol 类型的值直接转换,但是只允许显式强制类型转换,使用隐式强制类型转换会产生错误。
    这里附上常见值类型转换表:
转String转Number转布尔值
undefined“undefined”NaNfalse
null“null”0false
true“true”1
false“false”0
“”0false
“1.2”1.2true
“one”NaNtrue
0“0”false
-0“0”
1“1”true
Infinity“Infinity”true
-Infinity“-Infinity”true
NaN“NaN”false

原始值

原始值是固定而简单的值,是存放在栈(stack)中的简单数据段,也就是说,它们的值直接存储在变量访问的位置.详解可见js中的原始值是什么?

toString()与valueOf()

toString()方法的作用是返回对象的字符串表示。普通对象调用该方法会返回"[object Object]"。而很多类却封装了自己独有的toString()方法。例如(这里只介绍对象类型,基本类型前文已介绍):

  • Array的toString方法其实就相当于join(","),它是将数组的每个元素都转化为字符串,再使用逗号作为分隔符将他们连接起来;
  • Function的toString()方法就将用户定义的函数的转化为源代码的字符串;
  • Date类定义toString()方法会返回一个人类友好(且js可解析)的日期和时间的字符串;
  • RegExp类定义的toString()方法会将正则对象转化为一个看起来像正则字面量的字符串。
[1,2,3].toString()			// "1,2,3"
(function() {}).toString()		// "function () {}"
/\d+/g.toString()				// "/\\d+/g"
let d = new Date(2022,2,25)
d.toString()				// 'Fri Mar 25 2022 00:00:00 GMT+0800 (中国标准时间)'

valueOf()方法的作用对象转化为代表对象的原始值,一般普通对象调用该方法会返回对象本身,但对于以Number(),String(),Boolean()为构造函数的包装类对象会返回被包装的原始值;对于Array,Function,RegExp对象类型与普通对象类型处理一样。但对于Date类型而言,返回的是日期内部的日期表示,相当于调用getTime()方法。

let d = new Date(2022,2,25)
d.valueOf()		// 1648137600000
d.valueOf() === d.getTime()	// true

对象(含Function类型)到原始值转换算法(注意这里与类型转换的区别)

对象转化为其他类型的时较为复杂,js内部共定义了三种对象到原始值的基本算法:

偏字符串

该算法返回原始值,只要有可能就返回字符串。

  1. 该方法会优先尝试调用toString()方法,若该方法有定义且返回的值为原始值,则使用该值。否则下一步;
  2. 尝试调用valueOf()方法,若该方法有定义且返回值为原始值,则使用该值,否则下一步;
  3. 抛出type error错误。
偏数值

该算法返回原始值,只要有可能就返回数值。
该方法与偏字符串算法类似,但是先调用valueOf()方法再调用toString()方法。

无偏好

该算法不倾向于任何原始值类型,而是由类自己定义转换规则,js内部除了Date类实现了偏字符串算法,其余都实现了偏数值算法。

对象各种类型隐式转换规则

对象到布尔值

这个其实已经在上文介绍过,除了那几个值外其余均为true,包括包装对象new Boolean(false)

对象到字符串

在将对象转化为字符串时,会先使用偏字符串算法先将该对象转化为一个原始值,再将该原始值转化为字符串。

对象转化为数值

在将对象转化为数值时,会先使用偏数值算法先将该对象转化为一个原始值,再将该原始值转化为数值。

例子解释

Number([])			// 0
Number([9])		    // 9
Number(function a() {})		// NaN
Number({})					// NaN
let date = new Date(2022,2,25)
date == date.toString()			// true
date == date.valueOf()			// false
0 == new Number(0)				// true
  1. 首先’[]'的类型为对象类型,会先使用偏数值算法转化原始值的方法,调用valueOf()方法,而Array继承了默认的valueOf()方法,返回的数组本身,并不是原始值,因此调用toString()方法;因此[].toString()是"“,[9].toString()是"9”。再转化为Number类型,即 0 与 9.
  2. 首先function是Function类型实例对象,会先使用偏数值算法转化原始值的方法,调用valueOf()方法,而Array继承了默认的valueOf()方法,返回函数本身,并不是原始值,因此调用toString()方法;是"function a() {}",再转化为Number类型便是NaN。
  3. 首先’{}'的类型为对象类型,会先使用偏数值算法转化原始值的方法,调用valueOf()方法,而Obeject默认的valueOf()方法,返回的对象本身,并不是原始值,因此调用toString()方法;返回值为"[object Object]",再转化为数值自然就是NaN.
  4. 由于是’=='操作符,首先date为对象,使用无偏好原则,Date类型自带的偏向字符串原则,因此先调用datetoString()方法,得出该值是原始值,因此转换类型返回的是date.toString(),因此toString()为true,valueOf()false
  5. 由于是’=='操作符,首先new Number(0)为对象类型,使用无偏好原则,Number使用的是偏数值原则,因此先调用valueOf方法,得出该值0是原始值,因此转换类型返回的是0,与左侧值相等,因此返回为true
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值