JavaScript之类型转换

455 阅读9分钟

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

table th:first-of-type { width: 100px; } table th:nth-of-type(3) { width: 100px; }

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

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

注意:

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

案例:

// 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. 如果第一个字符在进制内,则解析到第一个超出进制的符号为止

案例:

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 开头的,先进行十六进制转换,在以第二个参数作为进制进行转换

案例:

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

案例:

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 基本相同

案例:

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:返回字符串

案例:

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

案例:

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

案例:

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 强制类型转换

案例:

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

2.2 运算符

运算符:+ - * /

2.2.1 + 运算符

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

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

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

案例:

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

案例:

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

案例:

[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

  4. Boolean 与其他类型比较,Boolean 先转换为 Number

  5. StringNumber 进行比较,String 转换为 Number

  6. 引用类型与基础类型进行比较,引用类型先转换为基础类型(调用 ToPrimitive)

  7. 引用类型与引用类型,直接判断是否指向同一对象

注意:

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

案例:

[] == ![]    // true
// ![] -> false
// 再运用规则一:false -> 0
// 再运用规则三:[] 调用 toPrimitive 返回 0
// 0 == 0   返回 true

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

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

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

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

案例:

//先调用 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. 实现进制互相转换
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. 生成随机字符串
let randomStr = Math.random().toString(36).slice(2)
console.log( randomStr )    // j40a2j457ojg   36进制可以包含所有字母
  1. 数组去重
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 )
  1. 函数深拷贝
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 隐式类型转换,一篇就够了!