一、浅拷贝
对象和数组的浅拷贝代码如下:
var obj1 = {
name: 'shen'
}
var obj2 = obj1
obj2.name = 'shenzhiyong'
console.log('obj1:', obj1) // obj1: {name: "shenzhiyong"}
console.log('obj2:', obj2) // obj2: {name: "shenzhiyong"}
var arr1 = [1,2,3]
var arr2 = arr1
arr2.push(4)
console.log('arr1:', arr1) // arr1: [1,2,3,4]
console.log('arr2:', arr2) // arr2: [1,2,3,4]
浅拷贝的意思就是只复制引用,没有复制真正的值。有时候我们只是想保留对象的数据,单纯想改变obj2和arr2的值,但是原对象的数据也发生了改变。很多时候这种情况都不是我们想要的。为了解决这个问题: 深拷贝它来了!
二、深拷贝
1、JSON方法
var obj1 = {
name: 'shen'
}
var obj2 = JSON.parse(JSON.stringify(obj1))
obj2.name = 'shenzhiyong'
console.log('obj1:', obj1) // obj1: {name: "shen"}
console.log('obj2:', obj2) // obj2: {name: "shenzhiyong"}
优点:简单明了,方便记忆
缺点:看下面代码。当对象里面出现函数的时候就不适用了。
var obj1 = {
name: 'shen',
show: function (argument) {
console.log(1)
}
}
var obj2 = JSON.parse(JSON.stringify(obj1))
console.log('obj1:', obj1) // obj1: {name: "shen", show: ƒ}
console.log('obj2:', obj2) // obj2: {name: "shen"}
2、手写递归方法
function deepCopy(obj) {
var newobj = obj.constructor === Array ? [] : {};
if (typeof obj !== 'object') {
return obj;
} else {
for (var i in obj) {
if (typeof obj[i] === 'object'){ //判断对象的这条属性是否为对象
newobj[i] = deepCopy(obj[i]); //若是对象进行嵌套调用
}else{
newobj[i] = obj[i];
}
}
}
return newobj; //返回深度克隆后的对象
}
var obj1 = {
name: 'shen',
show: function (argument) {
console.log(1)
}
}
var obj2 = deepCopy(obj1)
console.log('obj1:', obj1) // obj1: {name: "shen", show: ƒ}
console.log('obj2:', obj2) // obj2: {name: "shen"}
优点:能够实现对象和数组的深拷贝
缺点:如果拷贝的对象嵌套过深的话,会对性能有一定的消耗
3、es6解析结构 和 Object.assign()
let obj1 = {
a:1,
b:2,
c:{c1:1,c2:2}
}
// let obj2 = Object.assign({},obj1);
let obj2 = {...obj1};
obj2.a = 10;
obj2.c.c1 = 10;
console.log(obj1);
console.log(obj2);
// 运行结果
// {a: 1, b: 2, c: {…}}
// a: 1
// b: 2
// c: {c1: 10, c2: 2}
// __proto__: Object
// {a: 10, b: 2, c: {…}}
// a: 10
// b: 2
// c: {c1: 10, c2: 2}
// __proto__: Object
// 可以看到改变基本类型的值是没有变化的,但是当改变嵌套的引用类型时,它们会一起改变
优点:简单方便,适用于简单数据类型
缺点:只能拷贝基本类型的值,如果是引用类型那么拷贝出来的这个值还是属于潜拷贝
4、数组中的slice() & concat()
var arr1 = [1,2,3]
var arr2 = arr1.slice() // 方法一
// var arr2 = arr1.concat() //方法二
arr2.push(4)
console.log('arr1:', arr1) // arr1: [1, 2, 3]
console.log('arr2:', arr2) // arr1: [1, 2, 3, 4]
5、完全深度拷贝的方案
function cloneFun(obj) {
// 创建容器
let newobj = obj.constructor === Array?[]:{};
// 判断是否是对象类型
if( typeof obj !== 'object' ){
return obj;
}else{
for (const key in obj) {
if ( typeof obj[key] === 'object' ) {
newobj[key] = cloneFun(obj[key])
}else{
newobj[key] = obj[key];
}
}
}
return newobj;
}
let obj2 = cloneFun(obj1);
obj2.c.c1 = 10;
console.log(obj1);
console.log(obj2);
// 运行结果
// {a: 1, b: 2, c: {…}, d: ƒ}
// a: 1
// b: 2
// c: {c1: 1, c2: 2}
// d: ƒ ()
// __proto__: Object
// {a: 1, b: 2, c: {…}, d: ƒ}
// a: 1
// b: 2
// c: {c1: 10, c2: 2}
// d: ƒ ()
// __proto__: Object
// 改变任何值都不影响原始对象上的值
总结:在工作中看需求来选择解决方法,比如拷贝基本类型就用 json方法 、扩展运算符、或者Object.assign,如果有引用那么就用 函数递归拷贝