在 JavaScript 的八大内置数据类型中,Object 是唯一的一种引用类型(也叫复杂类型),其余七种(Undefined、Null、Boolean、Number、BigInt、String、Symbol)都属于基本类型。这一字之差,决定了它们在内存中的存储方式和行为模式完全不同。
本质:一组键值对的集合
Object 可以理解为一个容纳各种属性的袋子,每个属性都由一个键(属性名)和一个值(可以是任意类型,包括另一个对象或函数)组成。
const person = {
name: '张三', // 键是字符串 "name",值是字符串
age: 30, // 键是 "age",值是数字
greet: function() { // 键是 "greet",值是函数(此时也叫方法)
console.log('你好');
}
};
属性名通常是字符串(也可以是 Symbol),但非字符串类型的键会被自动转换为字符串。属性的值可以是基本类型、函数,甚至是另一个对象,这种嵌套构成了任意复杂的数据结构。
核心区别:存储与复制方式不同
理解引用类型的关键在于内存模型:
- 基本类型的值直接存储在栈内存中。当把一个基本类型赋值给另一个变量时,会复制一份独立的副本,两个变量互不影响。
- 引用类型的变量只保存对象在堆内存中的地址(引用),真正的对象数据存放在堆里。当把一个对象赋值给另一个变量时,复制的是地址引用,两个变量指向堆里的同一个对象。
let a = 10;
let b = a; // 复制值,b = 10
b = 20; // 修改 b 不影响 a
let obj1 = { value: 10 };
let obj2 = obj1; // 复制引用,obj2 指向同一个对象
obj2.value = 20; // 修改的是同一个对象
console.log(obj1.value); // 20,obj1 的值也变了
这个差异是新手最容易掉进的坑之一。当你传递对象给函数、将对象赋值给多个变量时,记住你一直在操作同一块数据。
比较方式:按引用比较
同样因为引用特性,两个看起来一模一样内容的对象,在比较时常常让人意外:
const a = { x: 1 };
const b = { x: 1 };
console.log(a === b); // false,因为比较的是引用地址,不是内容
console.log(a === a); // true,只有指向同一个对象才相等
这跟基本类型的值比较完全不同。想要比较两个对象的内容是否相等,必须逐层递归比较属性值(浅比较或深比较),没有内置的简单办法,这也是为什么前端状态管理库对整个不可变数据如此看重。
创建 Object 的常用方式
- 对象字面量(最常用):
const obj = { key: 'value' }; - new Object() 构造函数:
const obj = new Object(); - Object.create(proto):创建一个指定原型对象的新对象,在实现原型继承时非常有用。
const parent = { color: 'red' };
const child = Object.create(parent);
console.log(child.color); // 'red',来自原型
常用的 Object 静态方法
ECMAScript 为 Object 提供了大量实用方法,日常开发中出场率极高:
Object.keys(obj)– 返回对象自身可枚举属性的键名数组Object.values(obj)– 返回对象自身可枚举属性的键值数组Object.entries(obj)– 返回键值对数组,便于遍历Object.assign(target, ...sources)– 将多个源对象的属性复制到目标对象(浅拷贝)Object.freeze(obj)– 冻结对象,使属性不可修改、不可新增、不可删除Object.seal(obj)– 密封对象,不可新增/删除属性,但可修改现有属性值Object.prototype.hasOwnProperty()– 判断属性是否为自身属性(而非原型链上的)
属性访问与删除
对象的属性可以通过点表示法(obj.name)或方括号表示法(obj['name'])来访问。方括号的优势在于可以使用变量或包含空格的字符串作为键:
const key = 'dynamicKey';
const obj = { dynamicKey: 42 };
console.log(obj[key]); // 42
删除一个属性可以使用 delete obj.key,但要注意 delete 只是断开引用,不会直接释放内存,而且可能影响 V8 引擎的对象隐藏类优化(频繁删除属性会降低性能)。
引用类型的常见坑点
- 无意中的共享修改:多个地方持有同一个对象的引用,一处修改,处处受影响。
- const 声明的对象内容可以变:
const只保证引用地址不变,对象本身的属性仍然可以随意修改。 - 浅拷贝问题:
Object.assign或展开运算符{ ...obj }只复制一层属性的值,如果属性值是对象,仍然复制的是引用,内部对象还是共享的。
这些坑点恰好印证了“引用”二字的真正分量。理解 Object 的引用本质,是写出可预测的 JavaScript 代码的基础,也是后续深入学习原型链、深拷贝、不可变数据等话题的起点。