赋值 (完全一样,怎么改怎么变)
- 概念:直接将一个对象或数组的引用赋给另一个变量。
- 特点:新变量和原变量指向同一内存地址,改变其中一个会影响另一个。
浅拷贝 (半新,修改原数组子对象会跟着改变)
- 概念:创建一个新对象或数组,但只复制第一层属性。
- 实现方法:使用
Object.assign()
、扩展运算符(...
)等。 - 特点:对于嵌套对象,只有引用被复制,内层对象仍然共享同一引用,改变内层对象会影响原对象。
深拷贝。(完全是新的,修改和原数据无关)
- 概念:创建一个完全独立的新对象或数组,递归复制所有层级的属性。
- 实现方法:可以使用 JSON 方法(
JSON.parse(JSON.stringify(obj))
),或第三方库(如 Lodash 的cloneDeep
)。 - 特点:新对象与原对象完全独立,任何修改都不会相互影响。
总结
- 赋值:引用复制。
- 浅拷贝:一层复制,内层共享。
- 深拷贝:完全独立的复制。
栗子:
对子对象的影响主要取决于使用的赋值方式(赋值、浅拷贝或深拷贝)。以下是对每种方式的详细说明和示例,尤其是在处理嵌套对象时如何影响原始对象。
1. 赋值
示例:
let original = { a: 1, b: { c: 2 } };
let copy = original; // 赋值
copy.a = 10; // 修改顶层属性
copy.b.c = 20; // 修改子对象属性
console.log(original); // { a: 10, b: { c: 20 } }
影响:
copy
和original
引用相同的对象。修改copy
直接影响original
。
2. 浅拷贝
示例:
let original = { a: 1, b: { c: 2 } };
let shallowCopy = Object.assign({}, original); // 浅拷贝
shallowCopy.a = 10; // 修改顶层属性
shallowCopy.b.c = 20; // 修改子对象属性
console.log(original); // { a: 1, b: { c: 20 } }
影响:
shallowCopy
修改了a
,但original
的a
保持不变。- 但对
b.c
的修改会影响original
,因为b
仍然是一个引用。
3. 深拷贝
示例:
let original = { a: 1, b: { c: 2 } };
let deepCopy = JSON.parse(JSON.stringify(original)); // 深拷贝
deepCopy.a = 10; // 修改顶层属性
deepCopy.b.c = 20; // 修改子对象属性
console.log(original); // { a: 1, b: { c: 2 } }
影响:
deepCopy
的修改不会影响original
,因为两个对象是完全独立的。
总结
- 赋值:修改任意属性都会影响原始对象,因为它们共享同一引用。
- 浅拷贝:顶层属性的修改不会影响原始对象,但对子对象的修改会影响原始对象,因为它们共享同一引用。
- 深拷贝:所有修改都不会影响原始对象,两个对象完全独立。
浅拷贝的方法
1. 使用 Object.assign()
Object.assign()
方法用于将一个或多个源对象的可枚举属性复制到目标对象。返回目标对象。
const original = { a: 1, b: { c: 2 } };
const shallowCopy = Object.assign({}, original);
shallowCopy.a = 2; // 不影响 original
shallowCopy.b.c = 3; // 会影响 original
console.log(original.b.c); // 输出 3
2. 使用扩展运算符 (...
)
扩展运算符是 ES6 引入的一种简便语法,允许将一个对象的可枚举属性拷贝到另一个对象。
const original = { a: 1, b: { c: 2 } };
const shallowCopy = { ...original };
shallowCopy.a = 2; // 不影响 original
shallowCopy.b.c = 3; // 会影响 original
console.log(original.b.c); // 输出 3
3. 使用 Array.prototype.slice()
(用于数组)
对于数组,可以使用 slice()
方法来创建数组的浅拷贝。
const originalArray = [1, 2, { a: 3 }];
const shallowCopyArray = originalArray.slice();
shallowCopyArray[0] = 100; // 不影响 originalArray
shallowCopyArray[2].a = 200; // 会影响 originalArray
console.log(originalArray[2].a); // 输出 200
4. 使用 Array.from()
Array.from()
方法可以将类数组或可迭代对象转换为数组,并创建该数组的浅拷贝。
const originalArray = [1, 2, { a: 3 }];
const shallowCopyArray = Array.from(originalArray);
shallowCopyArray[0] = 100; // 不影响 originalArray
shallowCopyArray[2].a = 200; // 会影响 originalArray
console.log(originalArray[2].a); // 输出 200
5. 使用 Object.entries()
和 Object.fromEntries()
可以结合使用 Object.entries()
和 Object.fromEntries()
来实现对象的浅拷贝。
const original = { a: 1, b: { c: 2 } };
const shallowCopy = Object.fromEntries(Object.entries(original));
shallowCopy.a = 2; // 不影响 original
shallowCopy.b.c = 3; // 会影响 original
console.log(original.b.c); // 输出 3
总结
浅拷贝适合简单对象或数组的复制,但要注意对引用类型属性的修改可能会影响原对象。使用时,根据实际情况选择合适的方法。
深拷贝的方法
1. JSON 方法
let original = { a: 1, b: { c: 2 } };
let deepCopy = JSON.parse(JSON.stringify(original));
优点:简单易用。
缺点:无法处理函数、undefined、日期对象和正则表达式等。
2. 递归函数
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') return obj;
const clone = Array.isArray(obj) ? [] : {};
for (const key in obj) {
clone[key] = deepClone(obj[key]);
}
return clone;
}
let deepCopy = deepClone(original);
优点:可处理复杂数据类型。
缺点:可能会导致栈溢出(对于深层嵌套)。
3. Lodash 的 cloneDeep
let _ = require('lodash');
let deepCopy = _.cloneDeep(original);
优点:功能强大,支持多种数据类型。
缺点:需要引入第三方库。
4. 使用结构化克隆
let deepCopy = structuredClone(original);
优点:原生支持,能够处理大多数数据类型。
缺点:在旧版浏览器中不兼容。
总结
选择深拷贝方法时,要根据数据结构和项目需求来决定。对于简单对象,JSON 方法方便;对于复杂结构,使用递归函数或第三方库会更安全。