一: 装箱
我们会看到以下代码:
const str = 'hello world';
str.substring(0,3);
const num = 12.345;
num.toFixed(1);
众所周知,javascript只有Object类型才有方法。上面代码能执行正是因为自动装箱的结果。
什么是装箱呢?就是将基本类型转成对象类型的操作。分为显示装箱和隐式装箱。
显示装箱:
通过内置对象String、Boolean、Number等可以对基本类型进行显示装箱
const strObj = new String('hello world');
隐式装箱:
当读取一个基本类型值时,后台会为该基本类型创建一个对应的基本类型包装对象。在这个基本类型上调用方法,实际上是在这个基本类型包装对象上调用方法。这个基本类型包装对象是临时的,它只存在于方法调用那一行代码的的瞬间,一旦调用完毕就立即销毁。
num.toFixed(1);
// 在后台的具体执行如下:
const c = new Number(12.345);
c.toFixed(1);
c = null;
当我们访问 num 时,要从内存中读取这个数字的值,此时访问过程处于读取模式。在读取模式中,后台进行了三步处理:
-
创建一个 Number 类型的实例。
-
在实例上调用方法。
-
销毁实例。
二、拆箱
拆箱与装箱相反,将对象转变成基本类型的值。拆箱过程内部调用了抽象操作 ToPrimitive 。该操作接受两个参数,第一个参数是要转变的对象,第二个参数 PreferredType 是对象被期待转成的类型。第二个参数不是必须的,默认该参数为 number,即对象被期待转为数字类型。有些操作如 String(obj) 会传入 PreferredType 参数。有些操作如 obj + " " 不会传入 PreferredType。
具体转换过程是这样的。默认情况下,ToPrimitive 先检查对象是否有 valueOf 方法,如果有则再检查 valueOf 方法是否有基本类型的返回值。如果没有 valueOf 方法或 valueOf 方法没有返回值,则调用 toString 方法。如果 toString 方法也没有返回值,产生 TypeError 错误。
PreferredType 影响 valueOf 与 toString 的调用顺序。如果 PreferrenType 的值为 string。则先调用 toString ,再调用 valueOf。
具体测试代码如下:
var obj = {
valueOf : () => {console.log("valueOf"); return []},
toString : () => {console.log("toString"); return []}
}
String(obj)
// toString
// valueOf
// Uncaught TypeError: Cannot convert object to primitive value
obj+' '
//valueOf
//toString
// Uncaught TypeError: Cannot convert object to primitive value
Number(obj)
//valueOf
//toString
// Uncaught TypeError: Cannot convert object to primitive value