定义
浅拷贝(shallow copy):只复制指向某个对象的指针,而不复制对象本身,新旧对象共享一块内存。
深拷贝(deep copy):复制并创建一个一摸一样的对象,不共享内存,修改新对象,旧对象保持不变。
示例
1.
基本数据类型
// 基本数据类型
var a = 1;
var b = a;
b = 2;
console.log(a); // 1
console.log(b); // 2
- 基本数据类型(number、string、boolean,null、undefined)进行赋值时,按值传递。
- 当 b=a 赋值时,栈内存会新开辟一个内存,所以当修改b时,不会影响a。
注:基本数据类型,变量名和值都存储在栈内存中。
2.
引用数据类型
// 示例1 -- 数组
var a = [1, 2, 3, 4];
var b = a;
console.log(a === b);
a[0] = 0; // 修改数组a的值
console.log(a);
console.log(b);
// 示例2 -- 对象
var obj = {
name: 'Lucy',
age: 17
}
var newObj = obj;
newObj.name = 'Andy'; // 修改新的对象的属性name的值
console.log(obj);
console.log(newObj);
- 数组和对象进行赋值操作时,是按引用传值;
- 当赋值时,复制的仅是指针地址而已;
- 浅拷贝和深拷贝都只针对于像Object, Array这样的复杂对象;
- 区别:浅拷贝只复制对象的第一层属性、深拷贝可以对对象的属性进行递归复制。
注: 引用数据类型,变量名存在栈内存中,值存在于堆内存中,栈内存提供一个引用的地址指向堆内存中的值。
实现
1.
浅拷贝
① 对数组或对象进行普通的赋值。
var a = [1, 2, 3, 4];
var b = a;
console.log(a === b); // true
a[0] = 0; // 修改数组a的值
console.log(a); // [0, 2, 3, 4]
console.log(b); // [0, 2, 3, 4]
② Object.assign() --- 用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
Object.assign(target, ...sources)
参数:
target -- 目标对象
source -- 源对象
返回值
目标对象
var obj = {
student: {
name: 'Lucy',
age: 17
}
}
var newObj = Object.assign({}, obj);
newObj.student.name = "Andy";
console.log(obj);
console.log(newObj);
注:Object.assign() 只能实现非递归属性的一层的深度拷贝。
var obj1 = {
name: 'Lucy',
age: 17
};
var obj2 = Object.assign({}, obj1);
obj2.name = 'Andy';
console.log(obj1);
console.log(obj2);
2.
深拷贝
(1)数组
① slice() -- 从已有的数组中返回选定的元素。
arrayObject.slice(start,end)
参数
start -- 数组起始位置,包括此位置,如果负数,从数组尾部算起。
end -- 截取的结束位置,不包括此位置,如果负数,从数组尾部算起,如果省略,默认到结束所有元素。
返回值
新的数组 [start, end)
var arr1 = [1, 2, 3, 4];
var arr2 = arr1.slice(0);
arr2[0] = 0;
console.log(arr1);
console.log(arr2);
② concat() ---- 用于连接两个或多个数组。
arrayObject.concat(arrayX,arrayX,......,arrayX)
参数
arrayX -- 可以为具体的值或数组对象。
返回值
新的数组。
var arr1 = [1, 2, 3, 4];
var arr2 = arr1.concat();
arr2[0] = 0;
console.log(arr1);
console.log(arr2);
注:concat()和slice()函数只能实现数组仅有一层不存在递归属性的情况;如果数组存在递归属性,则此两种方法只能实现的为浅拷贝。
var arr1 = [1, [2, 3], 4];
var arr2 = arr1.slice();
arr2[1][0] = [5, 6];
console.log(arr1);
console.log(arr2);
(2)对象
① 手动复制遍历每个属性
var obj1 = {
name: "Lucy",
age: 17
};
var obj2 = new Object();
obj2.name = obj1.name;
obj2.age = obj1.age;
obj2.name = "Andy";
console.log(obj1);
console.log(obj2);
② Object.assign() -- 仅针对对象只有一层属性,不存在递归属性。
③ 转成JSON
JSON.stringify()把对象转成字符串;JSON.parse() 把字符串转成新的对象。
var obj1 = {
student: {
name: 'Lucy',
age: 17
}
}
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.student.name = 'Andy';
console.log(obj1 === obj2);
console.log(obj1);
console.log(obj2);
④ 递归
var obj1 = {
student: {
name: 'Lucy',
age: 17
}
}
function deepCopy(obj) {
var result = {};
for (var name in obj) {
if (typeof obj[name] === 'object') {
result[name] = deepCopy(obj[name]);
} else {
result[name] = obj[name];
}
}
return result;
}
var obj2 = deepCopy(obj1);
obj2.student.name = 'Andy';
console.log(obj1 === obj2);
console.log(obj1);
console.log(obj2);
⑤ JQuery的extend()方法 -- 用于将一个或多个对象的内容合并到目标对象。
$.extend( [deep ], target, object1 [, objectN ] )
参数
deep - 是否进行深度拷贝,默认为false
target- Object类型 目标对象,其他对象的成员属性将被附加到该对象上。
object1 - Object类型 第一个被合并的对象
返回值
目标对象
var arr1 = [1, 2, [3, 4], 5];
var arr2 = $.extend(true, [], arr1);
arr1[2][0] = [7, 8];
console.log(arr1);
console.log(arr2);
在默认情况下,通过$.extend()合并操作不是递归的(浅拷贝);如果第一个对象的属性本身是一个对象或数组,那么它将完全用第二个对象相同的key重写一个属性。这些值不会被合并。然而,如果将 true 作为该函数的第一个参数,那么会在对象上进行递归的合并(深拷贝)。
浅拷贝(false 默认):如果第二个参数对象有的属性第一个参数对象也有,那么不会进行相同参数内部的比较,直接将第一个对象的相同参数覆盖。
深拷贝(true):如果第二个参数对象有的属性第一个参数对象也有,还要继续在这个相同的参数向下一层找,比较相同参数的对象中是否还有不一样的属性,如果有,将其继承到第一个对象,如果没有,则覆盖。
var obj1 = {
student: {
name: 'Lucy',
age: '17',
gender: 'female'
},
grade: 1
}
var obj2 = {
student: {
name: 'Andy',
age: 18,
},
grade: 2,
hobby: 'football'
}
console.log($.extend(obj1, obj2));
var obj1 = {
student: {
name: 'Lucy',
age: '17',
gender: 'female'
},
grade: 1
}
var obj2 = {
student: {
name: 'Andy',
age: 18,
},
grade: 2,
hobby: 'football'
}
console.log($.extend(true, obj1, obj2));
参考
JS深拷贝与浅拷贝的区别,实现深拷贝的几种方法
JavaScript中浅拷贝和深拷贝的区别和实现
关于JavaScript的浅拷贝和深拷贝
JQuey.extend()的深拷贝和浅拷贝详细讲解