红宝书第四讲:JavaScript原始值与引用值行为差异详解
资料取自《JavaScript高级程序设计(第5版)》。
查看所有教程:红宝书学习大纲
一、基本定义与存储方式
原始值(Primitive Values)
引用值(Reference Values)
- 类型:对象(
Object
)、数组(Array
)、函数(Function
)等。 - 存储方式:实际数据存储在堆内存中,变量保存的是指向堆内存的指针地址1。
- 类型:对象(
二、变量复制时的差异
原始值的复制:独立拷贝
复制后,新旧变量完全独立。
let num1 = 5; let num2 = num1; // 复制值本身 num1 = 10; console.log(num2); // 输出5(num2不受num1修改影响)[^2]
引用值的复制:共享对象
复制的是指针地址,新旧变量指向同一个对象:
let obj1 = { name: "Alice" }; let obj2 = obj1; // 复制指针地址 obj1.name = "Bob"; console.log(obj2.name); // 输出"Bob"(通过指针修改同一对象)[^2]
三、动态属性操作能力
引用值可动态增删属性
对象可以随时添加或删除属性:
let person = {}; // 空对象 person.name = "Alice"; // 新增属性 delete person.name; // 删除属性[^4]
原始值无法添加属性
即使试图添加属性,也不会保留:
let name = "Charlie"; name.age = 30; // 不会报错,但无意义 console.log(name.age); // 输出undefined[^4]
四、类型检测与包装对象
类型检测工具
原始包装对象的特殊行为
原始值调用方法时临时创建对象包装器:
let s = "hello"; console.log(s.substring(1)); // 临时转换为String对象调用方法[^5]
包装对象与原始值的区别:
let a = "text"; let b = new String("text"); console.log(typeof a); // "string"(原始值) console.log(typeof b); // "object"(引用值)[^1]
五、典型陷阱:原始值与包装对象的逻辑差异
布尔值的逻辑陷阱
原始值
false
与对象new Boolean(false)
的不同行为:let bool1 = false; let bool2 = new Boolean(false); console.log(bool1 && true); // false(原值按false处理) console.log(bool2 && true); // true(对象按true处理)[^6]
总结核心差异
特性 | 原始值(如 5 , "text" ) | 引用值(如 {} , [] ) |
---|---|---|
存储位置 | 栈内存 | 堆内存(变量保存指针地址) |
复制行为 | 独立拷贝值 | 共享同一对象 |
属性动态修改 | 不允许 | 允许 |
类型检测 | typeof 正确区分 | instanceof 检测对象类型 |
原始包装对象特殊行为详细解释
一、原始值为什么能调用方法?
JavaScript 的基础类型(如 "hello"
)不是对象,但在调用方法时,会临时创建对应的包装对象,使原始值具备对象的行为:
let s = "hello";
console.log(s.substring(1)); // "ello"
实际发生了什么:
- 当调用
substring()
时,JS 引擎自动将s
转换为new String("hello")
(String 对象)。 - 调用完方法后,临时对象被销毁2。
- 当调用
二、原始值与包装对象的本质区别
通过 typeof
可以直观看出两者的存储方式不同:
let a = "text"; // 原始值
let b = new String("text"); // 包装对象
console.log(typeof a); // "string"(直接存储值)
console.log(typeof b); // "object"(指针,指向堆内存中的对象)[^1]
行为差异:
a
无法添加属性(如a.age = 20
无效)。b
可以动态增删属性(如b.age = 20
生效)。
三、典型陷阱:布尔值的逻辑判断
原始值和包装对象在逻辑运算中行为完全不同:
let bool1 = false; // 原始值
let bool2 = new Boolean(false); // 包装对象(本质是对象)
console.log(bool1 && true); // false(直接按原值 false 处理)
console.log(bool2 && true); // true(对象在逻辑运算中始终被视为 true)[^1]
原因解释:
- 所有对象(包括
new Boolean(false)
)在逻辑运算中会被强行转为true
。 - 原始值则严格按原值处理。
四、如何记住这些差异?
特征 | 原始值("text" ) | 包装对象(new String("text") ) |
---|---|---|
存储方式 | 直接存储值 | 存储指针,指向堆内存中的对象 |
方法调用 | 临时创建包装对象 | 直接调用 |
逻辑判断 | 按实际值(如 false ) | 始终视为 true (因为是对象) |
添加属性 | 无效 | 有效(操作真实对象) |
包装对象的键(key)与值(value)详解
1. new Boolean(false)
的键与值
- 关键点:
new Boolean(false)
实际创建的是一个 对象,内部存储了原始值false
,但其行为完全遵循对象的规则3。 键(keys):
无显式可枚举属性(自身属性)。通过Object.keys()
会返回空数组:let boolObj = new Boolean(false); console.log(Object.keys(boolObj)); // [](无直接属性)
值(value):
实际存储的原始值通过
valueOf()
获取:console.log(boolObj.valueOf()); // false(原始值)[^5]
但在布尔表达式中,对象始终被视为
true
:console.log(boolObj && true); // true(包装对象的逻辑行为)[^2]
2. new String("text")
的键与值
- 关键点:
new String("text")
会创建一个 字符串包装对象,内部存储字符串的字符序列和一个固定属性length
4。 键(keys):
包含字符索引(如0
,1
,2
,3
)和length
:let strObj = new String("text"); console.log(Object.keys(strObj)); // ["0", "1", "2", "3", "length"](不同环境中可能仅包含 "length")[^6]
值(value):
索引对应字符:
console.log(strObj[0]); // "t" console.log(strObj[1]); // "e"
length
表示字符长度:console.log(strObj.length); // 4 [^6]
valueOf()
返回原始字符串:console.log(strObj.valueOf()); // "text"(原始值)[^5]
原理总结
类型 | new Boolean(false) | new String("text") |
---|---|---|
本质上 | 对象(存储 false ) | 对象(存储字符序列和 length ) |
显式键(keys) | 无5 | 字符索引和 length 4 |
实际值 | false (需用 valueOf() ) | "text" (通过 valueOf() ) |
行为差异 | 逻辑运算中视为 true 3 | 字符和长度可直接访问4 |
目录:总目录
上篇文章:第三讲:JavaScript 操作符与流程控制详解
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。