curTain

js 中类型转换分为显式类型转换和隐式类型转换

  1. 显式类型转换方法:Number()parseInt()parseFloat()toString()StringBoolean()
  2. 隐式类型转换:逻辑运算符( &&、||、! )、运算符(+、-、*、/)、关系运算符(>、<、<=、>=)、相等运算符(==)、if/while 条件运算

一、显式类型转换

1.1 Number()

Number() 能将任意类型的参数转换为数值类型

规则如下:

  • 布尔值:true 和 false 分别被转换为 1 和 0
  • 数字:返回本身
  • null:返回 0
  • undefined:返回 NaN
  • 字符串:
    • 只包含数字(0X/0x开头的十六进制数字字符串,允许正负号),返回十进制数
    • 包含有效浮点数,返回浮点数
    • 空字符串,返回0
    • 不是以上形式的字符串,返回 NaN
  • Symbol:抛错
  • 对象:调用对象的 valueOf() 方法,然后依据前面的规则转换返回的值,如果转换的结果是 NaN,则调用对象的 toString() 方法,再次按照前面的规则转换返回的字符串

强调:

当对象转换为数字时,调用对象的 valueOf() 方法,然后依据前面的规则转换返回的值,如果转换的结果是引用类型,则调用对象的 toString() 方法,再次按照前面的规则转换返回的字符串,如果返回还是引用类型,最后返回 NaN

部分内置对象默认调用的 valueOf() 方法的行为:

对象 返回值 类型
Boolean 布尔值 基础类型
Date 返回毫秒数-从1970.1.1 0:0:0 开始计算 基础类型
Nunber 数字值 基础类型
String 字符串值 基础类型
Array 返回数组本身 对象类型
Object 对象本身 对象类型
Function 函数本身 对象类型

注意:

当数组只有一个元素时,会将第一个元素提取出来做转换

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Number() 类型转换
Number(true) // 1
Number(null) // 0
Number(undefined) // NaN
Number("0x15") // 21 0x15 -> 16+5=21
Number("") // 0
Number("-0X16") // NaN
Number(Symbol(12)) // error

// valueOf() 方法返回值
[1,2,3,4].valueOf() // [1, 2, 3, 4] 对象类型
Number( [1,2,3,4] ) // NaN
Number( [ 12 ] ) // 12
Number( [ "12" ] ) // 12

let empty = [].valueOf() // [] 对象类型
Number( empty ) // 0 [].valueOf()=[]->NaN, 再调用empty.toString()='' Number("") -> 0

Boolean().valueOf() // false
Number( Boolean() ) // 0

let date = new Date()
date.valueOf() // 1601345955850
Number( date ) // 1601345955850

function fn(){ var a = 1 }
fn.valueOf() // fn(){ var a = 1 }
Number( fn ) // NaN

let obj = { name: "tan" }
obj.valueOf() // {name: "tan"} // 对象本身
Number( fn ) // NaN

1.2 parseInt( param,radix )

传入两个参数,第一个是需要进行转换的参数,第二是转换的进制,最终返回十进制数字或NaN

1.2.1 第一个参数是字符串:

  1. 忽略字符串前面的空格,从非空字符开始计算
  2. 空字符串返回 NaN
  3. 第一个字符超过进制,返回 NaN,进制从0-9a-z开始计算,最大进制为 36
  4. 如果第一个字符在进制内,则解析到第一个超出进制的符号为止

案例:

1
2
3
4
5
6
parseInt("  0111")      // 111
parseInt("") // NaN
parseInt("1c", 13) // 25 13+12
parseInt("1cd", 13) // 25 13+12
parseInt("dc1", 13) // NaN d进制超过13
parseInt("-1cd", 13) // -25

1.2.2 第一个参数是数字:

  1. 第一个参数不是以 0 开头的,以第二个参数作为进制进行转换
  2. 第一个参数是以 0 开头的,先进行八进制转换,再以第二个参数作为进制进行转换
  3. 第一个参数是以 0X/0x 开头的,先进行十六进制转换,在以第二个参数作为进制进行转换

案例:

1
2
3
4
parseInt(111)       // 111
parseInt(13, 16) // 19 16+3
parseInt(013, 16) // 17 013->8+3=11 11->16+1=17
parseInt(0X13, 16) // 25 0X13->16+3=19 19->16+9=25

1.2.3 第一个参数是 nullundefined

返回 NaN

1.2.4 第一个元素是数组

取数组的第一个元素作为第一个参数,再进行进制转换

空数组返回 NaN

案例:

1
2
3
4
parseInt([11], 16)  // 17  16+1=17
parseInt([11, 12, 13, 23], 16) // 17

parseInt([]) // NaN

1.2.5 第一个参数是 Symbol 类型

抛出错误

1.3 parseFloat( string: string )

将参数转为浮点型数字,没有进制转换,转换规则与 parseInt 基本相同

案例:

1
2
parseFloat( "123.45abc" )   // 123.45
parseFloat( ["123.45abc", "78", "9"] ) // 123.45

1.4 toString() 方法

转换规则如下:

  1. Number类型:输出数字字符串,函数可以填入转换进制
  2. nullundefined:抛错
  3. 数组:将数组展开,空数组返回 ""
  4. 对象:返回 [object Object]
  5. Date:返回日期的文字表示法
  6. 函数:函数的字符串
  7. Symbol:输出 Symbol 字符串
  8. Boolean:返回字符串

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
let num = 29
num.toString() // '29'
num.toString(15) // '1e' 15+14 14->e
29.5 .toString(15) // '1e.7777777777778'

let nul = null
nul.toString() // 抛错

[1,2,3].toString() // '1,2,3'
[{name:"tan"},1,3].toString() // '[object Object],1,3'
[].toString() // ''

let obj = {name:"tan"}
obj.toString() // '[object Object]'

let date = new Date()
date.toString() // 'Tue Sep 29 2020 18:20:50 GMT+0800 (中国标准时间)'

function fn(){ console.log("tan") }
fn.toString() // 'function fn(){ console.log("tan") }'

let tan = Symbol("tan")
tan.toString() // 'Symbol(tan)'

true.toString() // 'true'

1.5 String( value?: any )

将参数转为字符串,与 toString 转换规则基本类似,只能传一个参数

toString() 不同,String() 可以转换 nullundefined

案例:

1
2
3
4
5
6
7
8
9
10
11
String(123)     // '123'
String(true) // 'true'
String({}) // '[object Object]'
String([]) // ""
String([1,2,3]) // '1,2,3'

String(null) // 'null'
String( undefined ) // 'undefined'

function fn(){ console.log("tan") }
String(fn) // 'function fn(){ console.log("tan") }'

1.6 Boolean( value?: any )

undefinednullfalse''0+-0NaN 转换为 false

其余都是 true,负数转为 true

案例:

1
2
3
4
5
6
7
Boolean(undefined)  // false
Boolean(null) // false
Boolean(0) // false
Boolean(-0) // false
Boolean(NaN) // false
Boolean(-34) // true
Boolean([]) // true

二、隐式类型转换

逻辑运算符( &&、||、! )、运算符(+、-、*、/)、关系运算符(>、<、<=、>=)、相等运算符(==)、if/while 条件运算

2.1 条件判断

&&、||、!、if/while 条件判断

会将数据转换成 Boolean 类型,转换规则同 Boolean 强制类型转换

案例:

1
2
3
if( [] )      // true
if( ![] ) // false
if( -1 ) // true

2.2 运算符

运算符:+ - * /

2.2.1 + 运算符

用于数字相加和字符串拼接

  1. 当一侧为 String 类型时,被识别为字符串拼接,优先将另一侧调用 String() 转换为字符串
  2. 当一侧为 Number 类型时,另一侧为原始类型时,将原始类型调用 Number() 转化为数字类型
  3. 当一侧为 Number 类型时,另一侧为引用类型时,将引用类型作为参数调用 String()Number 类型转换为字符串后拼接

以上三点,优先级从高到低

案例:

1
2
3
4
5
6
7
8
9
10
11
12
123 + "123"   // 123123
123 + true // 124
123 + null // 123
"1" + 4 + 5 + "6" // 1456

123 + {} // 123[object Object] {} -> String({}) -> [object Object]
123 + function fn(){} // 123function fn(){}

let nul = null
"tan" + nul // tannull null -> String(null) -> null

12 + [ 5, 4 ] // 125,4 [5, 4] -> String([5,4]) => 5,4

2.2.1 - * / 运算符

将各种非 Number 类型隐式调用 Number() 函数将值转换为数值类型,如果其中一个转换为 NaN,结果为 NaN

案例:

1
2
3
4
5
6
7
8
true + true // 2
2 - true // 3
5 - 012 // -5 012 -> Number(012) -> 8+2=10
5 - 0x12 // -13 0x12 -> Number(0x12) -> 16+2=18

5 - [ 2 ] // 3 [2] -> Number([2]) -> 2
5 - [ 2, 3 ] // NaN [2, 3] -> Number([2, 3]) -> NaN
5 - {} // NaN {} -> Number({}) -> NaN

2.3 关系操作符

关系操作符:==、<、>、<=、<=

2.3.1 <、>、<=、<= 操作符

  1. 两个操作值都是数值,进行数值比较
  2. 都是字符串,比较字符对应的字符编码值
  3. 一方是 Symbol,抛错
  4. 除了上述情况,都使用 Number() 函数进行隐式转换,再进行比较

注意:

NaN 与任何数值比较都不相等,都返回 false

案例:

1
2
3
4
5
6
7
8
[26] > [12]     // true     [26] -> Number([26]) -> 26    [24] -> 24 
"abc" < "acb" // true b < c
77 > "76" // true
77 > "76a" // false 76a -> Number("76a") -> NaN
77 > [] // true Number([]) -> 0

18 > "0x11" // true 0x11 -> Number(0x11) -> 16+1=17
17 > "0x11" // false 0x11 -> Number(0x11) -> 16+1=17

2.3.1 == 操作符

  1. NaN 不与其他任何值相等
  2. nullundefined 进行比较为 true
  3. nullundefined 与其他任何值比较结果都为 false
  1. Boolean 与其他类型比较,Boolean 先转换为 Number
  2. StringNumber 进行比较,String 转换为 Number
  3. 引用类型与基础类型进行比较,引用类型先转换为基础类型(调用 ToPrimitive)
  4. 引用类型与引用类型,直接判断是否指向同一对象

注意:

如果没有部署 [Symbol.toPrimitive] 接口,则先返回 valudeOf() 的值,若返回的不是基础类型,再返回 toString() 的值,若返回的不是基础类型的值,则抛出错误

案例:

1
2
3
4
5
6
7
8
9
10
[] == ![]    // true
// ![] -> false
// 再运用规则一:false -> 0
// 再运用规则三:[] 调用 toPrimitive 返回 0
// 0 == 0 返回 true

"[object Object]" = {} // true
// 引用类型与基础类型进行比较,引用类型先调用 ToPrimitive 函数,
// 没有 ToPrimitive 函数,则调用 {}.valueOf() 返回引用类型的值
// 返回值不是基础类型,继续调用 {}.toString() 返回 [object Object]

附加知识点–对象转换成原始数据类型

如果部署了 [Symbol.toPrimitive] 接口,那么调用此接口,若返回的不是基础数据类型,抛出错误。

如果没有部署 [Symbol.toPrimitive] 接口,那么先返回 valueOf() 的值,若返回的不是基础类型的值,再返回 toString() 的值,若返回的不是基础类型的值, 则抛出异常。

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//先调用 valueOf, 后调用 toString
let obj = {
[Symbol.toPrimitive]() {
return 200;
},
valueOf() {
return 300;
},
toString() {
return 'Hello';
}
}
//如果 valueOf 返回的不是基本数据类型,则会调用 toString,
//如果 toString 返回的也不是基本数据类型,会抛出错误
console.log(obj + 200); //400

总结

js 类型转换分为显式转换和隐式转换

通过本文可知,隐式转换也是间接的调用显式转换的方法来进行转换的,但是我们需要记住隐式转换的规则

在显式转换的方法中,需要注意的以下几点:

  1. Number( value: any ): 参数以 0 开头会进行八进制转换,以 0x/0X 开头会进行十六进制转换
  2. parseInt( value, radix ):参数是数组时,取第一个元素转换,与 Number() 处理数组的方式不同,传空数组时,会返回 NaN,而 Number([]) 会返回 0
  3. toString(): toString 方法可以将数字转为特定进制的字符串

显式转换的使用:

  1. 实现进制互相转换
1
2
3
4
let num = 29
let str = num.toString( 16 ) // 1d 16+13=29 13->d
let strToNum = parseInt( str, 16 )
console.log( str, strToNum ) // 1d 29
  1. 生成随机字符串
1
2
let randomStr = Math.random().toString(36).slice(2)
console.log( randomStr ) // j40a2j457ojg 36进制可以包含所有字母
  1. 数组去重

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    let arr = [ 123, 123, { name: "tan" }, [ 123 ], [ 1,2 ], { name: "tan" }, [ 1,2 ] ]

    let copyArr = arr.map( item => JSON.stringify( item ) )
    let flatArr = [ ...new Set( copyArr ) ]

    let newArr = flatArr.map( item => JSON.parse( item ) )

    // ["123", "123", "{"name":"tan"}", "[123]", "[1,2]", "{"name":"tan"}", "[1,2]"]
    console.log( copyArr )

    // ["123", "{"name":"tan"}", "[123]", "[1,2]"]
    console.log( flatArr )
  2. 函数深拷贝

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    function fn(){
    console.log( "hello" )
    console.log( "hello1111" )
    }
    let bodyStr = ""
    fn.toString().replace( /\{(.*)\}/sm, function( all, one ){
    bodyStr = arguments[1]
    return ""
    })
    let newFn = new Function( bodyStr )

    // hello
    // hello1111
    newFn()

写下这篇文章希望对你有帮助,共勉-

参考材料

刘小夕–JS 类型转换的规则是什么?

粥里有勺糖–offer收割机–js的隐式类型转换规则整理

freeCodeCamp–Javascript 隐式类型转换,一篇就够了!


 评论