引言
大家好,今天我们要谈论的是JavaScript中那些让人眼花缭乱的类型转换操作。在日常编程中,我们常常需要处理不同类型之间的转换,比如将数字转换为字符串、将布尔值转换为数字等等。而JavaScript中的类型转换机制又相当灵活,让人有时候啼笑皆非。废话不多说,让我们一起来揭开JavaScript类型转换的神秘面纱吧!
[] == ![] 将在讲述完原理后进行详细讲解。
原始类型互转
首先,让我们来看看JavaScript中的原始类型互转。在JavaScript中,原始类型包括数字、字符串、布尔值、null、undefined等。有时候我们需要将这些原始类型进行转换,这部分比较简单,看下表记住就好了。
参数类型 | 初始值 | Number(xx) | String(xx) | Boolean(xx) |
---|---|---|---|---|
Undefined | undefined | NaN | "undefined" | false |
Null | null | 0 | "null" | false |
Boolean | true | 1 | "true" | true |
Boolean | false | 0 | "false" | false |
Number | NaN | NaN | "NaN" | false |
Number | Infinity | Infinity | "Infinity" | true |
Number | 0 | 0 | "0" | false |
Number | -123 | -123 | "-123" | true |
Number | 123 | 123 | "123" | true |
String | "123" | 123 | "123" | true |
String | "-123" | -123 | "-123" | true |
String | "" | 0 | "" | false |
String | " " | 0 | " " | true |
原始类型转对象
接下来,我们来看看原始类型如何转换为对象。在JavaScript中,原始类型可以通过包装对象的方式转换为对应对象。
new Boolean()
new Number()
new String()
对象转原始类型(重点)
除了原始类型之间的转换,JavaScript还支持对象转原始类型的操作。对象转原始类型时,JS会调用对象的valueOf() 方法和toString() 方法和 ToPrimitive 来进行转换。所以我们先来了解一下这三种方法的原理。
valueOf()
仅用于包装类对象,返回包装对象的原始值
toString()
toString()方法有多个,JS引擎会根据不同情况使用不同的toString()方法。
- {}.toString() 得到由"[object 和 class 和 ]" 组成的字符串,也就是'[object Object]'
- [].toString() 返回由数组内部元素以逗号拼接的字符串
- "xx".toString() 返回字符串字面量
ToPrimitive
那么ToPrimitive是什么呢,这个时候你点击Annotated ES5官方文档,它就会跳转到下面这个页面:
这个时候官方文档又要你跳转到8.12.8(下图):
好长好长,其实总结下来就是:
ToPrimitive(obj,String) ==> String(obj)
- 如果接收到的是原始值,直接返回值
- 否则,调用toString 方法,如果得到原始值,返回
- 否则,调用valueOf 方法,如果得到原始值,返回
- 否则,报错
ToPrimitive(obj,Number) ==> String(obj)
- 如果接收到的是原始值,直接返回值
- 否则,调用valueOf 方法,如果得到原始值,返回
- 否则,调用toString 方法,如果得到原始值,返回
- 否则,报错
对象转String
从JS官方文档Annotated ES5(下图)可以知道,对象转String会先调用ToPrimitive(input argument, hint String) 方法,然后将它得到的结果再利用ToString(上方表格内规则)转化为String类型。
注意ToString与toString是不一样的
对象转Number
对象转Number与对象转String类似,只是ToPrimitive调用valueOf 和toString 方法的顺序不同。
对象转Boolean
对象转Boolean就比较简单了,根据Annotated ES5官方文档可知,所有对象转Boolean都是true
一元运算符 +
对于一元运算符 +,它的执行原理与ToNumber是一致的,也就是对象转Number。
+[]
以+[]为例,它的转换过程是这样的:
首先调用ToPrimitive(obj,Number)方法:
- [].valueOf() 返回[],不是原始值
- [].toString() 返回''(空字符串) 再然后调用ToNumber,得到0
二元运算符 +
对于一元运算符 +,它的执行原理是:
v1 + v2 流程:
- lprim = ToPrimitive(v1) rprim = ToPrimitive(v2)
- 只要lprim 和 rprim 有一个是字符串,则非字符串使用ToString转为字符串,再直接拼接
- 否则,ToNumber(lprim) + ToNumber(rprim)
ToPrimitive依照Number顺序规则
null + 1
它的转换过程是这样的:
- null 和 1 都是原始数据类型,所以直接返回
- null 和 1 都不是字符串所以执行 ToNumber(null) + ToNumber(1)
- 所以变为 0 + 1 ,返回 1
[] + {}
它的转换过程是这样的:
- lprim = ToPrimitive(v1) rprim = ToPrimitive(v2), 得到lprim='',rprim = '[object Object]'
- 有字符串拼接,所以返回 '[object Object]'
== 操作符
如果你查阅Annotated ES5官方文档,是这样的:
总结下来就是,如果是相同类型:
类型 | 条件 | 结果 |
---|---|---|
Undefined | Type(x) 和 Type(y) 都是 Undefined | 返回 true |
Null | Type(x) 和 Type(y) 都是 Null | 返回 true |
Number | ||
x 和 y 都是 NaN | 返回 false | |
x 和 y 是相同的数值 | 返回 true | |
x 是 +0,y 是 −0 | 返回 true | |
x 是 −0,y 是 +0 | 返回 true | |
其他情况 | 返回 false | |
String | x 和 y 是完全相同的字符序列(长度相同且对应位置的字符也相同) | 返回 true |
Boolean | x 和 y 都是 true 或都是 false | 返回 true |
相同对象 | x 和 y 引用 相同的对象 | 返回 true |
不同类型:
类型 | 条件 | 结果 |
---|---|---|
null 和 undefined | x 是 null 且 y 是 undefined | 返回 true |
undefined 和 null | x 是 undefined 且 y 是 null | 返回 true |
Number 和 String | x 是 Number 而 y 是 String | 返回 x == ToNumber(y) |
String 和 Number | x 是 String 而 y 是 Number | 返回 ToNumber(x) == y |
Boolean | x 是 Boolean | 返回 ToNumber(x) == y |
Boolean | y 是 Boolean | 返回 x == ToNumber(y) |
String 或 Number 和 Object | x 是 String 或 Number 而 y 是 Object | 返回 x == ToPrimitive(y) |
Object 和 String 或 Number | x 是 Object 而 y 是 String 或 Number | 返回 ToPrimitive(x) == y |
其他情况 | 返回 false |
[] == ![] 解读
下面回到我们的题目[] == ![]返回什么?为什么?
- ! 的优先级高于 == ,所以!先执行:
- !规则为先转化为Boolean 再取反,而[]为对象,所有对象转化为Boolean都为true,所以得到 [] == false
- 按照规则,类型不同,y为Boolean先进行x == ToNumber(y),得到 [] == 0
- 按照规则,类型不同,一边为Object,一边为Number,进行 ToPrimitive(x) == y,得到 '' == 0
- 按照规则,类型不同,一边为String,一边为Number, 进行 ToNumber(x) == y ,得到 0 == 0
- 最后返回 true
结语
通过今天的分享,相信大家对JavaScript中的类型转换有了更深入的了解。类型转换虽然有时候让人摸不着头脑,但正是这种灵活的类型转换机制,让JavaScript编程变得更加有趣和富有挑战性。希望大家在日常编程中能灵活运用类型转换的知识,让代码更加简洁高效!如果你觉得这篇文章有帮助或有所启发,别忘了给我一个鼓励的赞!