引子: 类型转换涉及到了拆箱装箱,那什么是拆箱装箱?如何进行拆箱装箱?
1.什么是装箱拆箱?
1-1.装箱转换
定义:基本类型 转化为对应的 对象
1-2.拆箱转换
定义:对象 转化为对应的 基本类型。JS标准规定了ToPrimitive用于拆箱转换。
2. 如何装箱?
2-1.前言
Number、String、Boolean、Symbol、BigInt 都在对象中有对应的类。
联想: 隐藏类map
仅在V8对象中存在,V8为每一个对象创建一个隐藏类,对象的map属性指向隐藏类。在V8内存空间中,Map区存放着隐藏类。(Map其实也可当作属于老生代)
(1)map定义:类似于静态语言的结构体,假设属性相对于对象的偏移量不会改变(无增删属性)
(2)常识:静态语言直接通过偏移量查询对象的属性值,而JS是动态语言,偏移量可能会发生变化,需查找。而隐藏类刚好解决了这个问题。
(3)map作用:提升对象的属性访问速度
(4)map内容:1.所有属性 2.每个属性相对于对象的偏移量
(5)重构map: 1.增加属性 2.删除属性 3.改变属性类型
(6)重构map会影响程序执行的性能。
(7)代码层面优化:1.避免增删属性 2.字面量初始化对象,属性顺序一致 3.避免改变属性类型。
(8)回收map: 当不再被任何对象引用时(即无相应结构的对象存在),会被回收。
2-2.Number、String、Boolean
Number、String、Boolean 构造函数 可与new 操作符搭配,进行显式装箱,产生对象。也可与 . 运算符 搭配,进行隐式装箱,产生对象。若可直接调用,则用于强制类型转换。
// 显式装箱
let a = new Number(1);
let b = new String('b');
let c = new Boolean(true);
// 隐式装箱
'lxh'.charAt(0);
2-3.Symbol、BigInt
Symbol、BigInt 构造函数 不可以与new搭配,可调用call进行隐式装箱(强制装箱),产生对象。
. 运算符提供装箱操作,根据基础类型构造一个[临时对象],能在基础类型上调用对应对象的原型上的方法。
注意:Object(Symbol())无法满足instaceof、construtor指向。
function createSymbolObject(description){
return (function () {
return this;
}).call(Symbol(description))
};
function createBigIntObject(description){
return (function () {
return this;
}).call(BigInt(description))
};
3. 装箱的原理
根据基础类型构造一个临时对象,能在基础类型上调用对应对象的原型上的方法。 该临时对象只存在于方法调用那一行代码执行的瞬间,执行方法后立刻被销毁。
缺点:装箱机制会频繁产生临时对象,尽量避免。
4. 如何拆箱?
4-1.规范
JS标准规定了ToPrimitive函数用于拆箱转换。ToPrimitive会首先调用valueOf 和 toString来获取基本数据类型。
ToPrimitive(input,preferedType?)是JS内部算法,input要转换的值,preferedType期待转换的类型。
input为基本数据类型,则直接返回;Number先调用valueOf(),String先调用toString()。
若两属性均无,或没有返回基本类型,则会产生类型错误 TypeError: Cannot convert object to primitive value
4-2.操作
定义valueOf和toString
复杂数据类型在隐式转换时,先调用 valueOf,再调用 toString。
空数组的toString()方法会得到空字符串,而空对象的toString()方法会得到字符串
[object Object]
let success_unpack = {
valueOf: () => 7,
toString: () => 8,
}
console.log(Number(success_unpack)); // 7 Number拆箱: valueOf, toString, TypeError
console.log(String(success_unpack)); // 8 String拆箱: toString, valueOf, TypeError