JavaScript类型转换

118 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情

前言

在学习winter大大的《重学前端》中,在js基础类型转换模块中,跟随知识点再次进行梳理,对其中的装箱和拆箱操作作了一定了解,整理成此文。如有错误,敬请交流指正。

基本转换规则

转换目标类型BooleanNumberStringObject
Nullfalse0'null'TypeError
UndefinedfalseNaN'undefined'TypeError
true(布尔型)-1'true'装箱转换
false(布尔型)-0'false'装箱转换
Number0/NaN --> false;其他为true-NumberToString装箱转换
String空字符串为false;其他为trueStringToNumber-装箱转换
SymboltrueTypeErrorTypeError装箱转换
Objecttrue拆箱转换拆箱转换-

StringToNumber(argument)

字符串到数字的类型转换,存在支持的语法结构,支持十进制,二进制,八进制和十六进制。 比如:30 ; 0b111 ; 0o13 ; 0xFF ;

进制英文范围前缀后缀
二进制Binary0-10BB
八进制Octal0-70OO
十进制Decimal0-9D
十六进制Hexadecimal0-9,A-F0xH

此外,js支持的字符串语法还包括正负号科学计数法,可以使用大写或小写的e来表示。 如:1e3 ; -1e-2 ;

阅读ECMA262文档, 转换为Number类型的操作: image.png

NOTE:需注意的是,parseInt和parseFloat和Number()转换规则不同;在不传入第二个参数的情况下,parseInt只支持16进制前缀“0x”,而且会忽略非数字字符,也不支持科学计数法。在一些古老的浏览器环境中,parseInt 还支持 0 开头的数字作为 8 进制前缀,这是很多错误的来源。所以在任何环境下,都建议传入 parseInt 的第二个参数,而 parseFloat 则直接把原字符串作为十进制来解析,它不会引入任何的其他进制。

多数情况下,Number 是比 parseInt 和 parseFloat 更好的选择。

NumberToString(argument)

在较小范围内,数字到字符串转换是符合我们直觉的十进制表示。

当Number绝对值较大或者较小时,字符串表示则是用科学计数法表示的

var a = 99999999999999999999999999999; 
a.toString( ); // "1e+29"

装箱转换

每一种基本类型 Number、String、Boolean、Symbol 在对象中都有对应的类(红宝书中称为原始值包装类型),所谓装箱转换,正是把基本类型转换为对应的对象,它是类型转换中一种相当重要的种类。

全局的 Symbol 函数无法使用 new 来调用,但我们仍可以利用装箱机制来得到一个 Symbol 对象,我们可以利用一个函数的 call 方法来强迫产生装箱。(call的第一个参数为基本类型时,会指向该原始值的自动包装对象)

var symbolObject = (function(){ return this; }).call(Symbol("a"));

console.log(typeof symbolObject); //object
console.log(symbolObject instanceof Symbol); //true
console.log(symbolObject.constructor == Symbol); //true

装箱机制会频繁产生临时对象,在一些对性能要求较高的场景下,我们应该尽量避免对基本类型做装箱转换。

使用内置的 Object 函数,我们可以在 JavaScript 代码中显式调用装箱能力。 每一类装箱对象皆有私有的 Class 属性,这些属性可以用 Object.prototype.toString 获取:

var symbolObject = Object(Symbol("a"));

console.log(Object.prototype.toString.call(symbolObject)); //[object Symbol]

拆箱转换

在 JavaScript 标准中,规定了 ToPrimitive 函数,它是对象类型到基本类型的转换(即拆箱转换)。

对象到 StringNumber 的转换都遵循“先拆箱再转换”的规则。通过拆箱转换,把对象变成基本类型,再从基本类型转换为对应的 String 或者 Number

拆箱转换会尝试调用 valueOftoString 来获得拆箱后的基本类型。如果 valueOftoString 都不存在,或者没有返回基本类型,则会产生类型错误 TypeError

var o = {
    valueOf : () => {console.log("valueOf"); return {}},
    toString : () => {console.log("toString"); return {}}
}

o * 2
// valueOf
// toString
// TypeError

我们定义了一个对象 o,o 有 valueOftoString 两个方法,这两个方法都返回一个对象,然后我们进行 o*2 这个运算的时候,你会看见先执行了 valueOf,接下来是 toString,最后抛出了一个 TypeError,这就说明了这个拆箱转换失败了。

到 String 的拆箱转换会优先调用 toString。我们把刚才的运算从 o*2 换成 String(o),那么你会看到调用顺序就变了。

var o = {
    valueOf : () => {console.log("valueOf"); return {}},
    toString : () => {console.log("toString"); return {}}
}

String(o)
// toString
// valueOf
// TypeError

在 ES6 之后,还允许对象通过显式指定 @@toPrimitive Symbol 来覆盖原有的行为。

var o = {
    valueOf : () => {console.log("valueOf"); return {}},
    toString : () => {console.log("toString"); return {}}
}

o[Symbol.toPrimitive] = () => {console.log("toPrimitive"); return "hello"}


console.log(o + "")
// toPrimitive
// hello

参考资料

  1. ECMA262
  2. JS类型转换之装箱操作
  3. JS类型转换之拆箱操作