红宝书第四讲:JavaScript原始值与引用值行为差异详解
资料取自《JavaScript高级程序设计(第5版)》。 查看所有教程:红宝书学习大纲
一、基本定义与存储方式
-
原始值(Primitive Values)
-
引用值(Reference Values)
- 类型 :对象(
Object
)、数组(Array
)、函数(Function
)等。 - 存储方式 :实际数据存储在堆内存中,变量保存的是指向堆内存的指针地址^1^。
- 类型 :对象(
二、变量复制时的差异
-
原始值的复制:独立拷贝
-
复制后,新旧变量完全独立。
javascriptlet num1 = 5; let num2 = num1; // 复制值本身 num1 = 10; console.log(num2); // 输出5(num2不受num1修改影响)[^2]
-
-
引用值的复制:共享对象
-
复制的是指针地址,新旧变量指向同一个对象:
javascriptlet obj1 = { name: "Alice" }; let obj2 = obj1; // 复制指针地址 obj1.name = "Bob"; console.log(obj2.name); // 输出"Bob"(通过指针修改同一对象)[^2]
-
三、动态属性操作能力
-
引用值可动态增删属性
-
对象可以随时添加或删除属性:
javascriptlet person = {}; // 空对象 person.name = "Alice"; // 新增属性 delete person.name; // 删除属性[^4]
-
-
原始值无法添加属性
-
即使试图添加属性,也不会保留:
javascriptlet name = "Charlie"; name.age = 30; // 不会报错,但无意义 console.log(name.age); // 输出undefined[^4]
-
四、类型检测与包装对象
-
类型检测工具
-
原始包装对象的特殊行为
-
原始值调用方法时临时创建对象包装器:
javascriptlet s = "hello"; console.log(s.substring(1)); // 临时转换为String对象调用方法[^5]
-
包装对象与原始值的区别:
javascriptlet a = "text"; let b = new String("text"); console.log(typeof a); // "string"(原始值) console.log(typeof b); // "object"(引用值)[^1]
-
五、典型陷阱:原始值与包装对象的逻辑差异
- 布尔值的逻辑陷阱
-
原始值
false
与对象new Boolean(false)
的不同行为:javascriptlet 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"
)不是对象,但在调用方法时,会临时创建对应的包装对象,使原始值具备对象的行为:
javascript
let s = "hello";
console.log(s.substring(1)); // "ello"
- 实际发生了什么 :
- 当调用
substring()
时,JS 引擎自动将s
转换为new String("hello")
(String 对象)。 - 调用完方法后,临时对象被销毁^2^。
- 当调用
二、原始值与包装对象的本质区别
通过 typeof
可以直观看出两者的存储方式不同:
javascript
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
生效)。
三、典型陷阱:布尔值的逻辑判断
原始值和包装对象在逻辑运算中行为完全不同:
javascript
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()
会返回空数组:javascriptlet boolObj = new Boolean(false); console.log(Object.keys(boolObj)); // [](无直接属性)
-
值(value):
-
实际存储的原始值通过
valueOf()
获取:javascriptconsole.log(boolObj.valueOf()); // false(原始值)[^5]
-
但在布尔表达式中,对象始终被视为
true
:javascriptconsole.log(boolObj && true); // true(包装对象的逻辑行为)[^2]
-
2. new String("text")
的键与值
-
关键点 :
new String("text")
会创建一个 字符串包装对象 ,内部存储字符串的字符序列和一个固定属性length
^4^。 -
键(keys) :
包含字符索引(如
0
,1
,2
,3
)和length
:javascriptlet strObj = new String("text"); console.log(Object.keys(strObj)); // ["0", "1", "2", "3", "length"](不同环境中可能仅包含 "length")[^6]
-
值(value):
-
索引对应字符:
javascriptconsole.log(strObj[0]); // "t" console.log(strObj[1]); // "e"
-
length
表示字符长度:javascriptconsole.log(strObj.length); // 4 [^6]
-
valueOf()
返回原始字符串:javascriptconsole.log(strObj.valueOf()); // "text"(原始值)[^5]
-
原理总结
类型 | new Boolean(false) |
new String("text") |
---|---|---|
本质上 | 对象(存储 false ) |
对象(存储字符序列和 length ) |
显式键(keys) | 无^[5](#类型 new Boolean(false) new String("text") 本质上 对象(存储 false) 对象(存储字符序列和 length) 显式键(keys) 无5 字符索引和 length4 实际值 false(需用 valueOf()) "text"(通过 valueOf()) 行为差异 逻辑运算中视为 true3 字符和长度可直接访问4 "#user-content-fn-5")^ | 字符索引和 length ^[4](#类型 new Boolean(false) new String("text") 本质上 对象(存储 false) 对象(存储字符序列和 length) 显式键(keys) 无5 字符索引和 length4 实际值 false(需用 valueOf()) "text"(通过 valueOf()) 行为差异 逻辑运算中视为 true3 字符和长度可直接访问4 "#user-content-fn-6")^ |
实际值 | false (需用 valueOf() ) |
"text" (通过 valueOf() ) |
行为差异 | 逻辑运算中视为 true ^[3](#类型 new Boolean(false) new String("text") 本质上 对象(存储 false) 对象(存储字符序列和 length) 显式键(keys) 无5 字符索引和 length4 实际值 false(需用 valueOf()) "text"(通过 valueOf()) 行为差异 逻辑运算中视为 true3 字符和长度可直接访问4 "#user-content-fn-2")^ |
字符和长度可直接访问^[4](#类型 new Boolean(false) new String("text") 本质上 对象(存储 false) 对象(存储字符序列和 length) 显式键(keys) 无5 字符索引和 length4 实际值 false(需用 valueOf()) "text"(通过 valueOf()) 行为差异 逻辑运算中视为 true3 字符和长度可直接访问4 "#user-content-fn-6")^ |
目录:总目录 上篇文章:第三讲:JavaScript 操作符与流程控制详解
Footnotes
-
原始值调用方法时的临时包装对象机制,文件 《JavaScript高级程序设计(第5版)》 第5章 ↩
-
Boolean包装对象的逻辑行为,文件 《JavaScript高级程序设计(第5版)》 第5章 ↩ ↩2
-
String包装对象的length属性与字符索引,文件 《JavaScript高级程序设计(第5版)》 第5章 ↩ ↩2 ↩3
-
包装对象的valueOf方法,文件 《JavaScript高级程序设计(第5版)》 第5章 ↩