原始值能调用方法靠自动装箱:引擎在属性或方法访问瞬间临时创建包装对象(如new String("hello")),执行后立即销毁;string、number、boolean、symbol、bigint有对应包装对象,null和undefined没有,调用方法会报TypeError。

原始值能调用方法,不是因为它“本来就是对象”,而是 JavaScript 引擎在你访问属性或调用方法的**那一瞬间**,自动创建了一个临时的包装对象——这个过程叫“装箱”。它不改变原始值,也不留下痕迹,执行完就销毁。
装箱:方法调用背后的隐形帮手
当你写 "hello".toUpperCase() 或 123.toString() 时,引擎实际做了三件事:
- 检测到操作对象是原始值(如字符串字面量、数字字面量)
- 临时构造对应包装对象(
new String("hello")或new Number(123)) - 在该临时对象上调用方法,然后立即丢弃它
所以你能取到 "abc".length,但写 "abc".custom = "x" 不生效——赋值发生在已销毁的对象上,原始值本身不可变、也无法添加属性。
哪些原始类型有包装对象?哪些没有?
只有 string、number、boolean、symbol、bigint 这五种原始类型拥有对应的包装对象(String、Number、Boolean、Symbol、BigInt)。
null 和 undefined 没有包装对象。对它们尝试调用任何方法(如 null.toString() 或 undefined.valueOf())都会直接报错:TypeError。
显式创建 vs 隐式装箱:行为差异很大
手动用 new String("hi") 创建的是真实对象,和原始值行为完全不同:
-
typeof "hi"→"string";typeof new String("hi")→"object" -
Boolean(new Boolean(false))→true(所有对象转布尔都是true) -
new String("a") === "a"→false(对象不等于原始值)
隐式装箱只在读取属性/方法时发生,且生命周期仅限单次操作;显式创建的对象会持续存在,直到被垃圾回收。
拆箱:包装对象如何回归原始值
当包装对象参与运算(比如相加、比较、JSON 序列化),引擎会把它“还原”回原始值,这叫“拆箱”。核心机制是按规范走 ToPrimitive 流程:
- 先尝试调用
valueOf(),若返回原始值,就用它 - 否则再调用
toString() - 都失败则抛出
TypeError
例如:new String("hi") + " there" → 先拆箱得 "hi",再拼接;JSON.stringify(new Number(42)) → 拆箱后输出 42(不是 {"value":42})。

















