为了转生而点技能-JavaScript,难题纪录(二)隐式转换规则及===、==

隐式转换规则

前言:

涉及隐式转换最多的两个运算子 + 和 ==。
+运算子即可数字相加,也可以字串相加。
== 不同於===,故也存在隐式转换。
减、乘、除这类运算子只会针对number型别,故转换的结果就是转换成number型别。


转换表

https://ithelp.ithome.com.tw/upload/images/20211123/20143762yUTVcyol3N.png


规则:

第一步:决定该转换成什麽类型:

加号(+)专属规则:

  1. 当一侧为String类型,加号(+)会被定义为字元串接,并会让另一侧优先转换为字串。
  2. 当一侧为Number类型,另一侧为原始类型(primiary type),原始类型会转换为Number型。
  3. 当一侧为Number类型,另一侧为引用类型(reference type:Array , Function , Object ),引用类型(一侧)和Number型(另一侧)皆转换成字串後串接。

优先顺序为由上而下!

第二步:需转换侧的值该如何转。

  1. 将值转为原始值,ToPrimitive():
    js引擎内部的抽象操作ToPrimitive有着这样的签名:ToPrimitive(input, PreferredType?)
    input是要转换的值,PreferredType是可选引数,可以是Number或String型别,由第一步决定。

2-1. PreferredType是Number型(第一步决定该转换为Number型)时:

  • 如果输入的值已经是一个原始值,则直接返回它
  • 否则,如果输入的值是一个物件,则呼叫该物件的valueOf()方法,如果valueOf()方法的返回值是一个原始值,则返回这个原始值。
  • 否则,呼叫这个物件的toString()方法,如果toString()方法返回的是一个原始值,则返回这个原始值。
  • 否则,丢掷TypeError异常。

2-2. PreferredType是String型(第一步决定该转换为String型)时:

  • 如果输入的值已经是一个原始值,则直接返回它
  • 否则,呼叫这个物件的toString()方法,如果toString()方法返回的是一个原始值,则返回这个原始值。
  • 否则,如果输入的值是一个物件,则呼叫该物件的valueOf()方法,如果valueOf()方法的返回值是一个原始值,则返回这个原始值。
  • 否则,丢掷TypeError异常。

2-3. PreferredType在第一步没有办法指定转换哪一种型时:


3-1.valueOf()方法:

  • Number、Boolean、String这三种建构函式生成的基础值的物件形式,通过valueOf转换後会变成相应的原始值。
  • Date这种特殊的物件,其原型Date.prototype上内建的valueOf函式将日期转换为日期的毫秒的形式的数值。
  • 除此之外返回的都为this,即物件本身。
var num = new Number('123');
num.valueOf(); // 123

var str = new String('12df');
str.valueOf(); // '12df'

var bool = new Boolean('fd');
bool.valueOf(); // true

var a = new Date();
a.valueOf(); // 1515143895500

var a = new Array();
a.valueOf() === a; // true

var b = new Object({});
b.valueOf() === b; // true

3-2.toString:

  • Number、Boolean、String、Array、Date、RegExp、Function这几种建构函式生成的物件,通过toString转换後会变成相应的字串的形式。
  • 其他物件返回的都是该物件的型别。
  • 参考转换表。

第三步:原始值转换。

经过前面步骤,会得到一个原始值,之後依照运算子的需求选择利用ToNumber将该原始值转换成数自型态;或是利用ToString将该原始值转换为字串,转换规则参考顶层的转换表。


试解:

第一题:

({} + {}) = ?
两个物件的值进行+运算子,肯定要先进行隐式转换为原始型别才能进行计算。

  1. 第一步:决定该转换成什麽类型:因为不属於+号的3个条件,属於2-3.之情况。
    1、第二步:ToPrimitive转换,由於没有指定PreferredType型别,{}会使预设值为Number,进行ToPrimitive(input, Number)运算。
    2、所以会执行valueOf方法,({}).valueOf(),返回的还是{}物件,不是原始值。
    3、继续执行toString方法,({}).toString(),返回"[object Object]",是原始值。
    故得到最终的结果,"[object Object]" + "[object Object]" = "[object Object][object Object]"

第二题:

2 * {} = ?
1、首先乘号运算子只能对number型别进行运算,故第一步就是对{}进行ToNumber型别转换。
2、由於{}是物件型别,故先进行原始型别转换,ToPrimitive(input, Number)运算。
3、所以会执行valueOf方法,({}).valueOf(),返回的还是{}物件,不是原始值。
4、继续执行toString方法,({}).toString(),返回"[object Object]",是原始值。
5、转换为原始值後再进行ToNumber运算,"[object Object]"就转换为NaN。
故最终的结果为 2 * NaN = NaN



严格相等(===)

定义:比较两侧的值是否在型态上及数值上皆相同。

NaN === NaN;  //false
+0 === -0;    //true

宽松相等(==)

定义:宽松相等不限於型态相同,即型态不相同也可宽松相等。

1 == '1' // true

运作方式:

  1. 一侧为boolean值的宽松相等比较:
    值首先会被转换为数字类型,根据boolean类型的ToNumber规则,true转为1,false转为0。
  2. 数字类型和字串类型的宽松相等比较:
    当数字类型和字串类型做宽松相等比较时,字串类型会被转换为数字类型;根据字串的ToNumber规则,如果是纯数字形式的字符串,则转为对应的数字,空字符转为0, 否则一律按转换失败处理,转为NaN。
    注意NaN不与任何值相等,包括它自己。
    注意字串内如果是16进位制,也算纯数字。
  3. 当引用型别和原始型别的宽松相等比较:
    对象类型会依照ToPrimitive规则转换为原始类型

试解:

第一题:

[2] == 2 // true

解:

  1. 属於引用型别和原始型别的宽松相等比较。
  2. [2]做ToPrimitive操作,因为[2]非data型(2-3.),所以PreferredType是Number型
  3. [2]也就是先调用valueOf,回传仍是[2]。
  4. [2]再调用toString,回传是'2'。
  5. '2'已经符合数字类型和字串类型的宽松相等比较之条件,ToNumber变成2。

第二题:

[] == !{} // true

1、! 运算子优先顺序高於==,故先进行!运算。
2、!{}运算结果为false,结果变成 [] == false比较。
3、属於一侧为boolean值的宽松相等比较:
等式右边y = ToNumber(false) = 0。结果变成 [] == 0。
4、属於当引用型别和原始型别的宽松相等比较:
[]会先呼叫valueOf函式,返回this。
不是原始值,继续呼叫toString方法,x = [].toString() = ''。
故结果为 '' == 0。
5、属於数字类型和字串类型的宽松相等比较:
等式左边x = ToNumber('') = 0。
所以结果变为: 0 == 0,返回true,比较结束。



参考文章:

  1. IT人-javascript 隐式转换:https://iter01.com/472967.html
  2. 程序前沿-从一道面试题说起—js隐式转换踩坑合集:https://codertw.com/%E7%A8%8B%E5%BC%8F%E8%AA%9E%E8%A8%80/701003/#outline__1
  3. JavaScript 隐式类型转换,一篇就够了!:https://chinese.freecodecamp.org/news/javascript-implicit-type-conversion/

<<:  做不好资金控管,一档大赔,获利全吐

>>:  软件行销 - 如何利用『免费使用』使公司获益

Day30 用python写UI-Canvas(二)

最後一天~~~ 压轴当然是要最好玩的东西啦,讲完今天的内容,大家就可以在介面上自由的创作了! ♠♣今...

验证 SQL Server Always On 设定

设定完成 AdventureWork 资料库做高可用性资料库後,来测试资料是否会正确的同步到次要资料...

# Day 30 Commencement: I open at the close

哇!不知不觉就到第 30 天了,来回顾一下这 30 天的旅程吧! 简单回顾 自己订的铁人赛主题是阅读...

# Day24--开分支免费啦!超简易开分支的方法

我还记得我学Git的时候,开始查Git是什麽,最开始出现的是下面这个画面,一堆原型的球球在上面,有的...

D28 - 走!去浏览器玩转黑胶唱片 Web Audio API

前言 今天玩转黑胶唱片,Audio Web API 接起来~~ Audio Web API Audi...