“一法三表”彻底记住JS显式/隐式强制类型转换

一、导读

由于各种历史原因javaScript的类型转换真的令人吐血。本文是老弟翻阅各种材料自己总结出的javaScript强制类型转换规则,整理了3张表和1个分析方法,便于记忆,小伙伴们可以先看结论,继续往下看分析有助于理解和记忆。
前言:首先要明白类型转换话题的范畴有多大?我们要讨论的是Boolean、String、Number、Array、Function、Date、Object这么多类型之间互相转来转去吗?当然不是,我们的目标只有2个:基本类型转成基本类型、复合类型转成基本类型。new String('hello')、new Number(1024)这种用装箱操作来把基本类型转成复合类型就不属于讨论的范畴(装箱甚至不能算是类型转换),再比如你让数字转成函数、数组转成函数本身就没意义。所以我们的目标就是基本类型转成基本类型、复合类型转成基本类型

二、结论

一法三表,指的是记住三张表格里的特殊转换,其他情况全都可以用一法分析出来。
一法:基本法。
基本法是我按照ES5规范的ToPrimitive方案抽离出来的叫法。步骤是:任何类型(基本类型和复合类型)做转换时先检查自己是否有valueOf()方法,如果有并且返回基本类型值,就使用该值做强制类型转换,如果valueOf()返回的还是复合类型,那就放弃转去调用toString()方法,把toString()返回的值做强制类型转换。如果valueOf()和toString()都不返回基本类型(或不存在)就做不了类型转换并且报TypeError错。
三表之一:各类型toString()和valueOf()方法返回值对照表:

表一

三表之二:各类型转成基本类型对照表:
表二

三表之三:所有隐式类型转换情形发生时的分析方法:

表三

三、分析

1、表一的分析。valueOf()和toString()的作用很重要

基本法就是完全依赖这2个方法的。
(1)JS在Boolean.prototype、String.prototype、Number.prototype、Symbol.prototype、Function.prototype、Date.prototype、Array.prototype、Object.prototype上都分别定义了各自的valueOf()和toString()方法。

(2)拿上面let num = 12举例,我们知道它的原型链是下图这样的,也就是所有基本类型都能用Object.prototype的toString()和valueOf()两个方法,拿上面let num = 12举例,如果JS没在Number.prototype上覆盖定义toString()和valueOf()的话,那么num.toString()的结果就是"[object Number]"了。正是因为全部的基本类型都覆盖了这2个方法,才有了表一五花八门的结果。


Number类型变量的原型链

(3)可以看到,除了Date对象的valueOf返回了当前时间到1970-1-1日的毫秒数(跟date.getTime()的效果一样)以外,其他8个全部和Object的效果一致——返回对象本身,其中Srting、Number、Boolean、Symbol这4个本身就是基本类型。注意这里的valueOf全都是对象各自覆盖后了的,而不是通过原型链找到的Object.valueOf(),尽管返回的结果是一样的。

(4)相反,所有的对象不仅覆盖了toString方法,还彻底改变了返回值,Array对象就是所有元素调用自己的toString再拼接起来;Date对象就是调用toDateString()和toTimeString()再拼接起来;其他都是直接加个引号。

(5)let num = 12和let num = new Number(12),虽然两个不相等,但是他们的toString和valueOf返回值是一样的。因为new Number(12)起到的作用是装箱,它的核心还是12这个数字。

(6)let num = 12,num.toString()和Object.prototype.toString().call( num )肯定是不一样的。

(7)因为valueOf()返回的都是对象本身,所以开发者直接取对象变量就好了,基本不会去调用valueOf(),基本都是自动被引擎调用。

2、表二的分析

(1)首先必须清楚的是表二里“转Number”、“转String”和“转Boolean”指的是采用Number()、String()、Boolean()这三个全局函数显式转换的结果。

(2)先看转String那一列,会发现除了null、undefined两个,其他类型的显式转换全部可以按照基本法转换得到(调用自己toString()得出的结果),这说明了显式类型转换和隐式转换的结果保持了一致,这个肯定得一致,要不然程序员得疯掉。null和undefined有点特殊,用String(null)会得到“null”字符串。

(3)再看转Number那一列,会发现所有的复合类型转成Number也可以按照基本法转换得到,所以复合类型要转成数字,都是一律先转成字符串,字符串再转成数字。比如[1]要转成数字,按照基本法首先看valueOf()返回数组对象本身,数组不是基本类型,所以调用toString()得到字符串“1”,“1”再转成数字是1。所以剩下的只要记住这一列null、undefined、Boolean和String类型这4种转成Number的结果就很轻松了。

(4)再看转Boolean那一列,转Boolean很好记忆,记住只有false和undefined、null、+0、-0、NaN、"",这7个值会转成false,其他一律转成true。

3、表三的分析

表一表二列举出的是每种类型转成基本类型的结果。表三就是为了说明将要发生类型转换时JS时怎么转的,"1" == 1 类型不一致肯定要转成一样的再比较,那么会把“1”转成数字呢?还是把1转成字符串呢?还是谁在==左边就转成对方的类型呢?

(1)单元+法、减乘除、取余、自增、自减这些只适用于数学计算,所以一律会转成数字。比如1-{age:30},{age:30}用基本法转成“[object Object]”再转成数字得到NaN,NaN+1还是NaN。

(2)==、!= 这两个宽松相等判断。==恐怕是面试官最爱问的了。看完这个分析再恶心的都是手到擒来。首先结论ES规范说明==除非中途两边类型一致了,否则最终都要转成数字。具体步骤是:
第1步:两边全部用基本法转成基本类型;
第2步:如果两边依然类型不一样,则统一转成数字再比较。
注意:用基本法转换后,不要管结果是什么,直接转成数字再比较。
比如 经典的[] == ![],!的优先级高于==,所以先算![],对照表二知道结果是false,所以变成判断 [] == false,用基本法转换[]转换成了“”(空字符串),变成判断“” == false,ok要转数字,对照表二,“”会转成0,false也转成数字0,所以最后 0==0 成立。

(3)双元+的情形要拎出来做典型,因为它可以用于数学加法和字符串拼接,比如1+2、“1”+“2”。所以双元+要看+号两边元素的类型。具体步骤是:
第1步:+号两边类型不一致,先用基本法转成基本类型;
第2步:如果+号两边的基本类型有一个是字符串,就把另一个转成字符串再拼接;
第3步:如果+号两边的基本类型都不是字符串,就都转成数字再相加。
比如1+{age:30},{age:30}用基本法转成“[object Object]”是字符串,所以把1转成“1”再拼接起来等于“1[object Object]”;再比如true+false,用基本法转一下,都得到自己,+号两边都是Boolean不是字符串,所以两边都转成数字,true转成1,false转成0,相加得到1。

(4)转Boolean类型的就对照表二就可以了。

4、特殊情况分析

(1)Date类型遇到双元+时。例如:let date = new Date(); let res = date + 1;实际的结果是输出"Fri Mar 20 2020 20:14:10 GMT+0800 (中国标准时间)1"。按道理这里date应该用基本法调用先调用valueOf()转换成基本类型1584670735713再做加法,但是没有,而是调用了toString()转换成了字符串再做拼接。
(2)Object.create(null)创建的对象做转换时。例如:let res = Object.create(null) + "hello"会报错Uncaught TypeError: Cannot convert object to primitive value。是因为用Object.create(null)创建的对象的原型是null,所以没有valueOf()和toString()方法,没法做类型转换。
(3)稀疏数组,即数组有空值时。看下面这个例子
var arr = [0]; arr[1] = undefined; arr[2] = null; arr[4]=4; 创建的数组是这样的[0,undefined,null,空值,3]。
arr.toString(); // "0,,,,4"
String(arr); // "0,,,,4"
String(arr[1]); // "undefined"
String(arr[2]); // "null"
String(arr[3]); // "undefined"
注意到,如果是直接调用数组的toString()或者用全局String()显式转换时,JS会把undefined、null、空值转成""空字符串参与拼接;而如果单独抠出来用String()做显式转换的话却会变成"undefined"、"null"和"undefined"。这还是值得注意的。

四、习题(做完不再怕面试)

小伙伴们可以按照本文一法三表的方法完成下面的习题,再去浏览器验证下。
1、[] + {}
2、{} + new Date()
3、[] - new Date()
4、{} + true
5、{} - false
6、[] + ""
7、[] + true + "2" + 3 - "99"
8 、null - undefined
8、null == false
9、null == undefined
10、undefined == false
11、true == "45"
12、false == 45
13、 "" == 0
14、 "" == false
15、0 == []
16、0 == {}
17、"0" == 0
18、"0" == false
19、"0" == []
20、[] == ![]
21、"" == [null]
22、2 == [2]
23、2 == ["2"]
24、1 == [true]

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,744评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,505评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,105评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,242评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,269评论 6 389
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,215评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,096评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,939评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,354评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,573评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,745评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,448评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,048评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,683评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,838评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,776评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,652评论 2 354

推荐阅读更多精彩内容