显式类型转换
显式类型转换是指通过调用特定的函数或者方法来进行类型转换。
-
转换为字符串
- String() 函数:可以把任意类型的值转换为字符串,不管是基本数据类型还是对象类型。
- toString() 方法:大部分对象都有 toString() 方法,能将对象转换为字符串。不过,null 和 undefined 没有 toString() 方法,调用会报错。
-
转换为数字
- Number() 函数:可把字符串、布尔值等类型转换为数字。如果转换失败,会返回 NaN。
- parseInt() 函数:用于将字符串转换为整数,它会从字符串的开头开始解析,直到遇到非数字字符为止。
- parseFloat() 函数:用于将字符串转换为浮点数,同样从字符串开头解析,能处理小数点。
-
转换为布尔值
-
Boolean() 函数:可将任意类型的值转换为布尔值。在 JavaScript 中,false、0、''、null、undefined、NaN 会被转换为 false,其余值转换为 true 。
隐式类型转换
隐式类型转换是指在某些运算或者操作时,JavaScript 自动进行的类型转换。
算术运算
-
加法运算(+)
-
若两个操作数都是数值:执行常规的数学加法。
-
若有一个操作数是字符串:另一个操作数会被转换为字符串,然后进行字符串拼接。
-
对于对象、数组、布尔值等其他类型:通常会先尝试转换为原始值,若无法转换为数值则转换为字符串。
// 数值与数值相加 - 略 // 字符串与字符串相加 - 略 // 字符串与其他类型相加 - 略
// 对象、数组与其他类型相加: // 对象/数组 先调用 valueOf() 方法,如果返回的不是原始值,再调用 toString() 方法。 // 对象与数值 let obj = { valueOf: function() { return 20 } } console.log(obj + 5) // 输出 25 // 对象与字符串 let obj2 = { toString: function() { return "Object" } } console.log("This is an " + obj2) // 输出 "This is an Object" // 数组与字符串 let arr = [1, 2, 3] console.log("Array: " + arr) // 输出 "Array: 1,2,3"
-
-
减法、乘法、除法和取模运算(-、*、/、%)
这些运算符通常期望操作数是数值类型。如果操作数不是数值,JavaScript 会尝试将其转换为数值类型,转换规则(Number() 函数转换规则)如下:
- 字符串:如果字符串只包含有效的数字字符,则转换为对应的数值;如果字符串为空,转换为 0;如果字符串包含非数字字符,转换为 NaN。
- 布尔值:true 转换为 1,false 转换为 0。
- null:转换为 0。
- undefined:转换为 NaN。
- 对象和数组:先调用 valueOf() 方法,如果返回的不是原始值,再调用 toString() 方法,然后将结果转换为数值。
比较运算
-
相等运算符(==)和不相等运算符(!=)
== 和 != 在进行比较时会进行隐式类型转换,将操作数转换为相同类型后再进行比较。
- null 和 undefined:null 只与 undefined 相等,即 null == undefined 为 true,与其他值比较都为 false。
- 布尔值与其他类型比较:布尔值会先转换为数值,true 转换为 1,false 转换为 0,然后再进行比较。
- 字符串与数值比较:字符串会尝试转换为数值,若转换成功则进行数值比较;若转换失败则结果为 false。
- 对象与原始值比较:对象会先调用 valueOf() 方法,如果返回的不是原始值,再调用 toString() 方法,然后将结果与另一个操作数进行比较。
-
全等运算符(===)和不全等运算符(!==)
=== 和 !== 不会进行隐式类型转换,只有当两个操作数的类型和值都相等时,=== 才返回 true;反之,!== 返回 true。
-
关系运算符(<、>、<=、>=)
关系运算符在比较时也会进行隐式类型转换。
- 两个操作数都是字符串:按字典序比较。
- 至少有一个操作数是数值:将另一个操作数转换为数值后进行比较。
- 对象参与比较:先将对象转换为原始值,再按照上述规则进行比较。
逻辑运算
-
逻辑非(!)
逻辑非运算符会把操作数转换为布尔值,然后取反。
- undefined、null、0、NaN、空字符串 '' 会被转换为 false,取反后为 true。
- 其他值会被转换为 true,取反后为 false。
-
逻辑与(&&)
逻辑与运算符会从左到右依次计算操作数,并且会把操作数隐式转换为布尔值来判断真假。
- 若第一个操作数转换为布尔值后为 false,则返回第一个操作数,不再计算第二个操作数。
- 若第一个操作数转换为布尔值后为 true,则返回第二个操作数。
-
逻辑或(||)
逻辑或运算符会从左到右依次计算操作数,并且会将操作数隐式转换为布尔值来判断真假。
- 若第一个操作数转换为布尔值后为 true,则返回第一个操作数,不再计算第二个操作数。
- 若第一个操作数转换为布尔值后为 false,则返回第二个操作数。
重点关注
Object 上 toString() 和 valueOf() 执行顺序与选择
-
顺序:先 toSring() 后 valueOf()
// 直接输出对象名 const obj = { valueOf: function() { console.log('调用 obj.valueOf') return '110' }, toString: function() { console.log('调用 obj.toString') return {} }, } alert(obj) // 【输出】调用 obj.toString 【输出】调用 obj.valueOf 【弹出】110 -
顺序:先 valueOf() 后 toSring()
算术运算、比较运算以及某些布尔值转换的复杂隐式类型转换场景
-
选择
// 构造函数实例 // 算术运算 调用原型上的valueOf // 直接输出 调用原型上的toString class Test { valueOf () { console.log('调用 valueOf 方法') return 'v' } toString() { console.log('调用 toString 方法') return 's' } } const TT = new Test() // 调用示例 TT + 1 // 输出:调用 valueOf 方法 `${TT}` // 输出:调用 toString 方法
经典案例
({}).valueOf() // {}
({}).toString() // "[object Object]"
([]).valueOf() // []
([]).toString() // ""
{} + {} // "[object Object][object Object]"
{} + [] // 0 => 解析器把 {} 理解为代码块, {} 不参与加法运算,实际执行的是 + []
({} + []) // "[object Object]"
({}) + [] // "[object Object]"
[] + {} // "[object Object]"
[] + [] // ""
+ {} // NaN
+ [] // 0
NaN !== NaN // true
+undefined // NaN