持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
前言
在学习winter大大的《重学前端》中,在js基础类型转换模块中,跟随知识点再次进行梳理,对其中的装箱和拆箱操作作了一定了解,整理成此文。如有错误,敬请交流指正。
基本转换规则
| 转换目标类型 | Boolean | Number | String | Object |
|---|---|---|---|---|
| Null | false | 0 | 'null' | TypeError |
| Undefined | false | NaN | 'undefined' | TypeError |
| true(布尔型) | - | 1 | 'true' | 装箱转换 |
| false(布尔型) | - | 0 | 'false' | 装箱转换 |
| Number | 0/NaN --> false;其他为true | - | NumberToString | 装箱转换 |
| String | 空字符串为false;其他为true | StringToNumber | - | 装箱转换 |
| Symbol | true | TypeError | TypeError | 装箱转换 |
| Object | true | 拆箱转换 | 拆箱转换 | - |
StringToNumber(argument)
字符串到数字的类型转换,存在支持的语法结构,支持十进制,二进制,八进制和十六进制。 比如:30 ; 0b111 ; 0o13 ; 0xFF ;
| 进制 | 英文 | 范围 | 前缀 | 后缀 |
|---|---|---|---|---|
| 二进制 | Binary | 0-1 | 0B | B |
| 八进制 | Octal | 0-7 | 0O | O |
| 十进制 | Decimal | 0-9 | 无 | D |
| 十六进制 | Hexadecimal | 0-9,A-F | 0x | H |
此外,js支持的字符串语法还包括正负号科学计数法,可以使用大写或小写的e来表示。 如:1e3 ; -1e-2 ;
阅读ECMA262文档,
转换为Number类型的操作:
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 函数,它是对象类型到基本类型的转换(即拆箱转换)。
对象到 String 和 Number 的转换都遵循“先拆箱再转换”的规则。通过拆箱转换,把对象变成基本类型,再从基本类型转换为对应的 String 或者 Number。
拆箱转换会尝试调用 valueOf 和 toString 来获得拆箱后的基本类型。如果 valueOf 和 toString 都不存在,或者没有返回基本类型,则会产生类型错误 TypeError。
var o = {
valueOf : () => {console.log("valueOf"); return {}},
toString : () => {console.log("toString"); return {}}
}
o * 2
// valueOf
// toString
// TypeError
我们定义了一个对象 o,o 有 valueOf 和 toString 两个方法,这两个方法都返回一个对象,然后我们进行 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