在 JavaScript 中实现深拷贝和浅拷贝可以采用不同的方法。下面分别介绍这两种拷贝方式的实现方式
浅拷贝(shallow copy)是一种复制对象或数组的操作,创建一个新的对象或数组,并将原始对象或数组的属性或元素的引用复制到新的对象或数组中。这意味着原始对象和拷贝对象会共享相同的引用类型数据,而不是创建新的引用类型数据。
浅拷贝只复制对象或数组的一层属性或元素,而不会递归地复制嵌套的对象或数组。当修改拷贝对象的属性或元素时,原始对象的对应属性或元素也会受到影响。因此,浅拷贝适用于简单的数据结构,但不适用于复杂的嵌套结构。
下面介绍几种常见的浅拷贝方法:
扩展运算符(Spread Operator)使用扩展运算符可以快速进行浅拷贝,将原始对象的属性或数组的元素展开到新的对象或数组中。
const originalObject = { name: 'John', age: 30 };
const shallowCopy = { ...originalObject };
const originalArray = [1, 2, 3];
const shallowCopy = [...originalArray];
Object.assign() 方法 使用 Object.assign() 方法可以将原始对象的属性复制到新的对象中。
const originalObject = { name: 'John', age: 30 };
const shallowCopy = Object.assign({}, originalObject);
const originalArray = [1, 2, 3];
const shallowCopy = Object.assign([], originalArray);
Array.prototype.slice() 方法 对于数组,可以使用 slice() 方法来创建一个新的数组,并将原始数组的元素复制到新的数组中。
const originalArray = [1, 2, 3];
const shallowCopy = originalArray.slice();
深拷贝(deep copy)是一种复制对象或数组的操作,创建一个全新的对象或数组,并递归地复制原始对象或数组的所有属性或元素,包括嵌套的对象或数组。深拷贝创建的副本是完全独立的,对副本的修改不会影响原始对象或数组。
深拷贝适用于复杂的数据结构,包括嵌套的对象或数组。当需要对数据进行修改、操作或传递给其他函数时,深拷贝可以确保原始数据的完整性和不变性。
下面介绍几种常见的深拷贝方法:
递归复制
通过递归遍历对象或数组,并复制所有的属性或元素,可以实现深拷贝。
function deepCopy(obj) {
if (typeof obj !== 'object' || obj === null) {
return obj; // 非对象类型直接返回
}
const copy = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (Object.hasOwnProperty.call(obj, key)) {
copy[key] = deepCopy(obj[key]);
}
}
return copy;
}
const originalObject = { name: 'John', age: 30 };
const deepCopyObject = deepCopy(originalObject);
const originalArray = [1, 2, { nested: true }];
const deepCopyArray = deepCopy(originalArray);
JSON 序列化与反序列化
可以使用 JSON 对象的 stringify() 方法将原始对象或数组转换为 JSON 字符串,再使用 parse() 方法将 JSON 字符串转换为新的对象或数组。这种方法可以实现简单的深拷贝,但有一些限制,例如无法复制函数、正则表达式和特殊的对象类型。
const originalObject = { name: 'John', age: 30 };
const deepCopyObject = JSON.parse(JSON.stringify(originalObject));
const originalArray = [1, 2, { nested: true }];
const deepCopyArray = JSON.parse(JSON.stringify(originalArray));
需要注意的是,使用 JSON 序列化与反序列化的方式进行深拷贝时,原始对象或数组中的函数、正则表达式、特殊对象类型等会被忽略或转换为字符串,丢失原始的数据类型和行为。
无论使用哪种深拷贝方法,都要注意以下几点:
const originalObject = { name: 'John', age: 30 };
const deepCopyObject = _.cloneDeep(originalObject);
const originalArray = [1, 2, { nested: true }];
const deepCopyArray = _.cloneDeep(originalArray);
解构赋值(destructuring assignment)是一种通过解构语法将数组或对象的属性值赋给变量的方式。它在 JavaScript 中引入了一种简洁而强大的方式来从复杂的数据结构中提取值。
解构赋值可以应用于数组和对象,并且支持多种用法和语法。下面详细介绍解构赋值的各种用法
数组解构赋值
通过数组解构赋值,可以将数组的元素值分别赋给对应的变量。
const [a, b, c] = [1, 2, 3];
console.log(a); // 输出 1
console.log(b); // 输出 2
console.log(c); // 输出 3
可以省略数组中的某些元素,只提取需要的部分。
const [a, , c] = [1, 2, 3];
console.log(a); // 输出 1
console.log(c); // 输出 3
还可以使用默认值来处理在解构过程中未定义的值。
const [a, b, c = 0] = [1, 2];
console.log(a); // 输出 1
console.log(b); // 输出 2
console.log(c); // 输出 0(使用默认值)
对象解构赋值
通过对象解构赋值,可以从对象中提取属性值并赋给对应的变量。
const { name, age } = { name: 'John', age: 30 };
console.log(name); // 输出 'John'
console.log(age); // 输出 30
可以使用别名来为提取的属性值指定新的变量名。
const { name: personName, age: personAge } = { name: 'John', age: 30 };
console.log(personName); // 输出 'John'
console.log(personAge); // 输出 30
也可以使用默认值处理在解构过程中未定义的属性值。
const { name, age = 0 } = { name: 'John' };
console.log(name); // 输出 'John'
console.log(age); // 输出 0(使用默认值)
函数参数的解构赋值
可以在函数参数中使用解构赋值,从传入的对象或数组中提取所需的值。
function greet({ name, age }) {
console.log(`Hello, ${name}! You are ${age} years old.`);
}
const person = { name: 'John', age: 30 };
greet(person); // 输出 'Hello, John! You are 30 years old.'
其他高级用法
剩余运算符(Rest Operator):可以使用剩余运算符 ...
将剩余的数组元素或对象属性提取为一个新的数组或对象。
const [a, ...rest] = [1, 2, 3, 4, 5];
console.log(a); // 输出 1
console.log(rest); // 输出 [2, 3, 4, 5]
const { name, ...rest } = { name: 'John', age: 30, city: 'New York' };
console.log(name); // 输出 'John'
console.log(rest); // 输出 { age: 30, city: 'New York' }
嵌套解构赋值:可以在数组和对象的解构赋值中进行嵌套,以提取多层嵌套的值。
function greet({ name, details: { age } }) {
console.log(`Hello, ${name}! You are ${age} years old.`);
}
const person = { name: 'John', details: { age: 30 } };
greet(person); // 输出 'Hello, John! You are 30 years old.'
解构赋值的嵌套用法可以在函数参数中灵活应用,以提取更复杂的数据结构。
function greet({ name, details: { age } }) {
console.log(`Hello, ${name}! You are ${age} years old.`);
}
const person = { name: 'John', details: { age: 30 } };
greet(person); // 输出 'Hello, John! You are 30 years old.'