Reference
Reference
变量存储的是变量的引用,而不是对象本身。
引用的复制
- 对象是“通过引用”被存储和复制的
- 字符串,数字,布尔值等 —— 始终是以“整体值”的形式被复制的。
当一个对象变量被复制 —— 引用被复制,而该对象自身并没有被复制。
例如:
let user = { name: "John" };
let admin = user; // 复制引用
let user = { name: 'John' };
let admin = user;
admin.name = 'Pete'; // 通过 "admin" 引用来修改
alert(user.name); // 'Pete',修改能通过 "user" 引用看到
引用的比较
当 a
和 b
两个变量都引用同一个对象时,它们相等:
et a = {};
let b = a; // 复制引用
alert( a == b ); // true,都引用同一对象
alert( a === b ); // true
而这里两个独立的对象则并不相等,即使它们看起来很像(都为空):
let a = {};
let b = {}; // 两个独立的对象
alert( a == b ); // false
对象的复制
因为 JavaScript 没有提供对此操作的内建的方法。但很少需要这样做 —— 通过引用进行拷贝在大多数情况下已经满足了。
但是,如果我们真的想要这样做,那么就需要创建一个新对象,并通过遍历现有属性的结构,在原始类型值的层面,将其复制到新对象,以复制已有对象的结构。
就像这样:
let user = {
name: "John",
age: 30
};
let clone = {}; // 新的空对象
// 将 user 中所有的属性拷贝到其中
for (let key in user) {
clone[key] = user[key];
}
// 现在 clone 是带有相同内容的完全独立的对象
clone.name = "Pete"; // 改变了其中的数据
alert( user.name ); // 原来的对象中的 name 属性依然是 John
我们也可以使用 Object.assign 方法来达成同样的效果。
语法是:
Object.assign(dest, [src1, src2, src3...])
- 第一个参数
dest
是指目标对象。 - 更后面的参数
src1, ..., srcN
(可按需传递多个参数)是源对象。 - 该方法将所有源对象的属性拷贝到目标对象
dest
中。换句话说,从第二个开始的所有参数的属性都被拷贝到第一个参数的对象中。 - 调用结果返回
dest
。
例如,我们可以用它来合并多个对象:
let user = { name: "John" };
let permissions1 = { canView: true };
let permissions2 = { canEdit: true };
// 将 permissions1 和 permissions2 中的所有属性都拷贝到 user 中
Object.assign(user, permissions1, permissions2);
// 现在 user = { name: "John", canView: true, canEdit: true }
如果被拷贝的属性的属性名已经存在,那么它会被覆盖:
let user = { name: "John" };
Object.assign(user, { name: "Pete" });
alert(user.name); // 现在 user = { name: "Pete" }
我们也可以用 Object.assign
代替 for..in
循环来进行简单克隆:
let user = {
name: "John",
age: 30
};
let clone = Object.assign({}, user);
它将 user
中的所有属性拷贝到了一个空对象中,并返回这个新的对象。
还有其他克隆对象的方法,在后面的章节中我们会讲到。
深拷贝
属性可以是对其他对象的引用。
我们应该使用一个拷贝循环来检查 user[key]
的每个值,如果它是一个对象,那么也复制它的结构。这就是所谓的“深拷贝”。
我们可以使用递归来实现它。或者为了不重复造轮子,采用现有的实现,例如 lodash 库的 _.cloneDeep(obj)。
const obj
user
的值是一个常量,它必须始终引用同一个对象,但该对象的属性可以被自由修改。
总结
对象通过引用被赋值和拷贝。换句话说,一个变量存储的不是“对象的值”,而是一个对值的“引用”(内存地址)。因此,拷贝此类变量或将其作为函数参数传递时,所拷贝的是引用,而不是对象本身。
所有通过被拷贝的引用的操作(如添加、删除属性)都作用在同一个对象上。
为了创建“真正的拷贝”(一个克隆),我们可以使用 Object.assign
来做所谓的“浅拷贝”(嵌套对象被通过引用进行拷贝)或者使用“深拷贝”函数,例如 _.cloneDeep(obj)。