🫣 总结
- 包装类与原始值转换过程叫做「装箱」和「拆箱」,装箱是将值类型包装为对象类型,拆箱是将对象类型转换为类型。
- 装箱和拆箱是 JS 来应对「在值类型数据上调用对象方法」的处理技术,使得基础类型与相应的对象类型被统一。
- 至于为什么要装箱和拆箱,个人理解:从内存使用的角度来看,是为了更好的性能。值类型存在栈里,引用类型存在堆里,在使用值类型的时候从栈里取的速度快,且大部分时候不需要调用对象类型中的方法,调用的时候用包装类型包装一下,调用完删除。
以下部分为摘录:
🛎️ 面向对象的妥协
JavaScript中存在两套类型系统,其一是基础类型系统(Basetypes),是由typeof运算来检测的;其二是对象类型系统(Objecttypes),对象类型系统是 object 中的一个分支。
面向对象的语言通常认为“一切都是对象”。于是在“对象类型系统”中就出现了一个问题:如果是这样,那么number基础类型与Number对象类型,以及其他基础类型与相应的对象类型是如何被统一的呢?
为了实现“一切都是对象”的目标,JavaScript在类型系统上做出了一些妥协,
其结果是∶ 为部分基础类型系统中的“值类型”设定对应的包装类,通过包装类,将“值类型数据”作为对象来处理。
这样一来,基础类型数据通过包装类转换而来的结果,和对象类型系统中的每一个实例一样,都成了理论上的“对象”。
- typeof obj 的值为 object 或 function
- obj instanceof Object 的值为 true
所以在值类型数据经过 “包装类” 包装后得到的是对象和原来的值类型数据不再是同一数据,只是二者同等的价值而已。
📫 显式装箱
- 显式创建,通过基本包装类型对象对基本类型进行显示装箱。
例如:new Number(3) // Number{3}
从语言的实现来说,这与传统语言中的“类型强制转换”完全不同:强制转换是在同一数据(相同内存地址的不同引用)的基础上进行的,但上述语法将创建一个新的数据。
尽管值类型中的“符号类型(symbol)”存在对应的包装类,但是它不能通过这种显式创建的语法来得到对象实例。
- 显式装箱,JavaScript内建的 Object() 类支持显式地将 boolean、number、string 和 symbol 四种值类型数据包装成对应的对象,这一语法在语义上解释为「基于值来创建等同的对象」。
例如:new Object(3) // Number{3}
装箱可以对new出来的对象进行属性和方法的添加,因为通过new操作符创建的引用类型的实例,在执行离开当前作用域之前一直保留在内存中。
📪 隐式装箱
对于隐式装箱,我们看下下面的代码:
var s1 = 'call_me_R'; // 隐式装箱
var s2 = s1.substring(2);
上面代码的执行步骤其实是这样的:
- 创建String类型的一个实例;
- 在实例中调用制定的方法;
- 销毁这个实例。
上面的三个步骤转换为代码,如下:
# 1
var s1 = new String('call_me_R');
# 2
var s2 = s1.substring(2);
# 3
s1 = null;
隐式装箱当读取一个基本类型值时,后台会创建一个该基本类型所对应的基本包装类型
对象。在这个基本类型的对象上调用方法,其实就是在这个基本类型对象上调用方法。这个基本类型的对象是临时的,它只存在于方法调用那一行代码执行的瞬间,执行方法后立即被销毁。这也是在基本类型上添加属性和方法会不识别或报错