一、浅拷贝
在JavaScript中,拷贝可以看成是将一个数据类型中的值,复制到另一个数据类型中的过程。浅拷贝是将原始对象第一层属性的内存地址复制到目标对象的内存地址中,换言之,浅拷贝只是复制了内存中的地址,两边操作同一块内存,也因此在浅拷贝中,改变其中一个变量可能会对另一个变量产生影响。
举个例子:
let obj1 = { a: 1, b: { c: 2 } }; let obj2 = Object.assign({}, obj1); obj1.a = 4; console.log(obj2.a); // 输出:1
在上面的例子中,我们将obj1复制到obj2中,然后修改obj1的a属性为4,那么obj2的a属性不会被改变,还是保持原来的1。这是因为,Object.assign()是浅拷贝,只是将第一层属性值复制到了新的变量中,所以obj2中的值是和obj1相同,但是两者是不同的变量。
二、深拷贝
相对浅拷贝,深拷贝就是将原始对象的所有属性(包括第一层和嵌套的属性)递归地复制到目标对象中。在JavaScript中,我们通常使用递归的方式来实现深拷贝。
举个例子:
let obj1 = { a: 1, b: { c: 2 } }; let obj2 = JSON.parse(JSON.stringify(obj1)); obj1.b.c = 3; console.log(obj2.b.c); // 输出:2
在上面的例子中,我们使用JSON.stringify将obj1转成字符串,再用JSON.parse将其转成对象,这样就可以实现深拷贝了。当我们改变obj1的b属性中的c属性的值时,obj2并不会被改变,因为obj1和obj2是不同的变量。
三、浅拷贝的实现方式
JavaScript中,我们可以使用以下方式来实现浅拷贝:
1. Object.assign()
Object.assign()方法可以将一个或多个源对象的所有可枚举属性复制到目标对象中,并返回目标对象。只有第一层属性值被复制,如果存在相同属性,后面的属性会覆盖前面的属性。注意,Object.assign()是浅拷贝,只复制第一层属性。
let obj1 = { a: 1, b: { c: 2 } }; let obj2 = Object.assign({}, obj1);
2. 扩展运算符
扩展运算符可以将一个对象的可枚举属性拷贝到另一个对象中,同样只能实现浅拷贝。
let obj1 = { a: 1, b: { c: 2 } }; let obj2 = {...obj1};
四、深拷贝的实现方式
JavaScript中,我们可以使用以下方式来实现深拷贝:
1. 递归实现
递归实现深拷贝是最常用的实现方式。通过递归将嵌套对象的每一层都复制到新的变量中,从而实现了完整的拷贝。需要注意的是,在遇到循环引用或者包含函数等其他类型时,需要进行特殊处理。
function deepClone(obj){ if(typeof obj !== 'object' || obj === null){ return obj; } let newObj = Array.isArray(obj) ? [] : {}; for(let key in obj){ newObj[key] = deepClone(obj[key]); } return newObj; } let obj1 = { a: 1, b: { c: 2 } }; let obj2 = deepClone(obj1);
2. JSON.parse & JSON.stringify
我们前面已经提到过,可以使用JSON.stringify将对象转成字符串,然后再用JSON.parse将其转成对象,从而实现深拷贝。需要注意的是,使用JSON.stringify & JSON.parse无法拷贝函数和循环引用。
let obj1 = { a: 1, b: { c: 2 } }; let obj2 = JSON.parse(JSON.stringify(obj1));
3. Lodash库
Lodash是一个流行的JavaScript实用工具库,其中提供了_.cloneDeep方法,支持深拷贝。
let obj1 = { a: 1, b: { c: 2 } }; let obj2 = _.cloneDeep(obj1);
五、总结
在JavaScript中,浅拷贝和深拷贝是两种常用的拷贝方式。浅拷贝只复制第一层的属性,复制后的变量和原变量指向同一块内存;深拷贝则递归复制所有的属性,复制后的变量和原变量是完全独立的变量。浅拷贝可以使用Object.assign和扩展运算符等简单方式实现,深拷贝则需要使用递归、JSON.parse & JSON.stringify或者Lodash库等方式实现。