在 JavaScript 中,对象和数组都是引用类型,拷贝时如果只是拷贝了引用,那么源对象和目标对象就会共享同一块内存地址,这就是浅拷贝。而深拷贝则是将源对象完全复制一份,并重新分配一块新的内存地址给目标对象,两者之间不再共享任何内存地址。
举例说明:
// 浅拷贝示例
let obj1 = {a: 1, b: {c: 2}};
let obj2 = obj1; // 浅拷贝
obj2.a = 3;
console.log(obj1); // 输出 {a: 3, b: {c: 2}}
console.log(obj2); // 输出 {a: 3, b: {c: 2}}
obj2.b.c = 4;
console.log(obj1); // 输出 {a: 3, b: {c: 4}}
console.log(obj2); // 输出 {a: 3, b: {c: 4}}
// 深拷贝示例
let obj3 = {a: 1, b: {c: 2}};
let obj4 = JSON.parse(JSON.stringify(obj3)); // 深拷贝
obj4.a = 3;
console.log(obj3); // 输出 {a: 1, b: {c: 2}}
console.log(obj4); // 输出 {a: 3, b: {c: 2}}
obj4.b.c = 4;
console.log(obj3); // 输出 {a: 1, b: {c: 2}}
console.log(obj4); // 输出 {a: 3, b: {c: 4}}
在上面的浅拷贝示例中,当我们修改了 obj2.a
的值时,obj1.a
的值也发生了变化,这是因为两者共享同一块内存地址。而当我们修改了 obj2.b.c
的值时,obj1.b.c
的值也跟着发生了变化。
在深拷贝示例中,我们使用了 JSON.parse(JSON.stringify(obj3))
对 obj3
进行了深拷贝。当我们修改了 obj4.a
的值时,obj3.a
的值没有发生变化,因为它们各自占用了不同的内存地址。而当我们修改了 obj4.b.c
的值时,obj3.b.c
的值也没有发生变化,因为此时它们所占用的内存地址也已经不同了。
在 ES6 中,.map()
方法是浅拷贝。这意味着它将不会对原数组进行深层次的复制。
当使用 map()
方法时,它将遍历原始数组并创建一个新的数组,该数组包含与原数组相同数量的元素,但是每个元素都是通过调用回调函数来映射原数组中对应元素而生成的。在这个过程中,仅对数组中的每个元素进行了浅拷贝,也就是说复制了对象的引用而不是对象本身。
这意味着如果原始数组中的元素是对象或数组,则新数组中的相应元素将指向相同的对象或数组。因此,如果在新数组中修改了这些对象或数组中的任何一个,那么原数组中对应的元素也会受到影响。
举个例子:
const originalArray = [{name: 'Alice'}, {name: 'Bob'}];
const newArray = originalArray.map(item => item);
newArray[0].name = 'Charlie';
console.log(originalArray[0].name); // Output: 'Charlie'
在上面的代码中,我们首先创建了一个原始数组 originalArray
,它包含两个对象。然后我们使用 map()
方法创建了一个新数组 newArray
,其中包含相同的元素。接下来,我们修改了 newArray
中第一个元素的 name
属性。由于 map()
方法只进行了浅拷贝,因此 originalArray
中对应元素的 name
属性也被修改了。
如果希望将 ES6 中的 .map()
方法转换为深拷贝,可以利用 JSON.parse()
和 JSON.stringify()
来实现。具体步骤如下:
- 对原始数组使用
JSON.stringify()
方法将其转换为字符串形式; - 将上一步中得到的字符串使用
JSON.parse()
方法转换为一个新的对象或数组; - 对该对象或数组调用
map()
方法,并在回调函数中对每个元素进行递归处理以确保进行深层次的复制; - 返回映射后的新数组。
举个例子:
const originalArray = [{name: 'Alice', hobbies: ['reading', 'hiking']}, {name: 'Bob', hobbies: ['swimming', 'biking']}];
const newArray = JSON.parse(JSON.stringify(originalArray)).map(item => {
// 对象进行了深拷贝
const newItem = Object.assign({}, item);
// 数组进行了深拷贝
newItem.hobbies = item.hobbies.slice(0);
return newItem;
});
newArray[0].hobbies.push('cooking');
console.log(newArray[0].hobbies); // Output: ['reading', 'hiking', 'cooking']
console.log(originalArray[0].hobbies); // Output: ['reading', 'hiking']
在上面的代码中,我们首先创建了一个原始数组 originalArray
,其中包含两个对象,每个对象都有一个 hobbies
属性,它是一个字符串数组。然后我们使用 JSON.stringify()
方法将其转换为字符串形式,再使用 JSON.parse()
方法将其转换回对象或数组的形式。这一步操作会创建一个新的、与原始数组数据完全独立的复制品。
接下来,我们对该复制品调用 .map()
方法,并在回调函数中对每个元素进行递归处理以确保进行深层次的复制。对于对象类型的元素,我们使用 Object.assign()
和空对象进行深拷贝;对于数组类型的元素,我们使用 slice()
方法进行浅拷贝。
最后,我们修改了 newArray
中第一个元素的 hobbies
属性,而不影响原始数组中的元素。这是因为我们已经完成了深拷贝。