一、 什么是克隆
其实js里的克隆跟生活里说的克隆是一样的。有一个本体,克隆出跟本体一摸一样的副体,当本体“受伤”时副体是不会“受伤”的,js的克隆也是如此。
1.1 浅克隆与深克隆
在js中对象克隆就是把对象值重新赋给一个新对象。克隆也存在深克隆于浅克隆的区别。引用类型的值都会被保存在堆内存中,在栈内存中会存在一个指针指向堆内存中的值。这时,如果只复制了指针,则可以说这个克隆为浅克隆,如果时根据指针找到具体的值,复制值,就可以称之为深克隆。
1.2 克隆简单实现
var benti = {
top: "头",
hand: "手",
leg: "腿",
};
console.log(
"本体有--"+benti.top+"、"+benti.hand+"、"+benti.leg
);
想要克隆出一个克隆体应该怎么做呢?有些人就想到直接将本体赋值给克隆体
var benti = {
top: "头",
hand: "手",
leg: "腿",
};
var kelongti = benti;
console.log(
"本体有--"+benti.top+"、"+benti.hand+"、"+benti.leg
);
console.log(
"克隆体有--"+kelongti.top+"、"+kelongti.hand+"、"+kelongti.leg
);
输出结果
从输出结果来看确实是“克隆”出来一个克隆体,上面说过克隆出来的克隆体是不会因为本体“受伤”而“受伤”的,那再看下面代码
var benti = {
top: "头",
hand: "手",
leg: "腿",
};
var kelongti = benti;
benti.top = "本体的头受伤了"; //本体的头受伤了
console.log(
"本体有--"+benti.top+"、"+benti.hand+"、"+benti.leg
);
console.log(
"克隆体有--"+kelongti.top+"、"+kelongti.hand+"、"+kelongti.leg
);
输出结果
按道理来说本体受伤,克隆体不应该受伤的呀!为什么会这样呢?
因为在内存中,对象存储是存在一个地址中的,这样直接赋值给副体,相当于直接把地址值给了副本,并不是克隆一个新的地址值,所以说这样子的做法并不是克隆。接下来我们要谈谈浅克隆和深克隆
二、 浅克隆
什么是浅克隆
只克隆第一级属性,如果某个属性又是一个内嵌的子对象,不会进入子对象中克隆子对象的内容。 这里用for in 来实现浅克隆
var benti = {
top: "头",
hand: "手",
leg: "腿",
};
//浅克隆
function clone(oldObj) {
var newObj = {};
for (var key in oldObj) {
newObj[key] = oldObj[key];
}
return newObj;
}
var kelongti = clone(benti);
benti.top = "本体的铁头受伤了"; //本体的头受伤了
console.log(
"本体有--"+benti.top+"、"+benti.hand+"、"+benti.leg
);
console.log(
"克隆体有--"+kelongti.top+"、"+kelongti.hand+"、"+kelongti.leg
);
输出结果
克隆出来的克隆体体与本体没有联系,即使改变本体的属性值,克隆体也不会被被改变这就是浅克隆
这样就达到了基本的浅克隆,但是这个浅克隆有些小问题需要注意,如果有些人想向克隆函数传一个null,数字,字符串,数组呢?不能都返回一个对象给别人吧!我们是想别人想克隆什么类型的就返回什么类型的给别人。那可以改一改上面的代码
var benti = {
top: "头",
hand: "手",
leg: "腿",
};
//浅克隆
function clone(oldObj) {
//如果传进来的参数是null,那就返回一个null
if (oldObj == null) return null; //直接退出
if (typeof oldObj != "object") {
return oldObj; //直接退出
} else {
var newObj = Array.isArray(oldObj) ? [] : {}; //不要直接创建对象,先使用三目运算符判断是创建数组还是对象
for (var key in oldObj) {
newObj[key] = oldObj[key];
}
return newObj; //返回新对象或数组
}
}
//克隆一个对象
var kelongti = clone(benti);
benti.top = "本体的头受伤了"; //本体的头受伤了
console.log(
"本体有--"+benti.top+"、"+benti.hand+"、"+benti.leg
);
console.log(
"克隆体有--"+kelongti.top+"、"+kelongti.hand+"、"+kelongti.leg
);
//参数为null
var boor = clone(null);
console.log(boor);
//参数为数字,字符串
var number = clone(12);
var string = clone("string");
console.log(number);
console.log(string);
//参数为数组
var arr = clone([1, 2, 3, 4, 5]);
console.log(arr);
输出结果
三、 深克隆
什么是深克隆
既可以克隆第一级属性,如果某个属性又是一个内嵌的子对象,深克隆会进入子对象中,继续克隆内嵌子对象及其内容。
什么是内嵌的子对象呢?看下面的代码
var benti = {
top: "头",
leg: "腿",
hand: "手",
miaoshu: {
shang: "大头",
xia: "长腿",
zhong: "粗手",
},
};
对象benti中的miaoshu就是内嵌的子对象,如果用浅克隆的方式克隆出来的克隆体是有问题的,因为对象存储是存在一个地址中,所以浅克隆是无法克隆内嵌的子对象,那深克隆是怎么实现的呢?是利用递归的思想来解决这个问题的
var benti = {
top: "头",
leg: "腿",
hand: "手",
miaoshu: {
shang: "大头",
xia: "长腿",
zhong: "粗手",
},
};
//深克隆
function clone(oldObj) {
//如果传进来的参数是null,那就返回一个null
if (oldObj == null) return null; //直接退出
if (typeof oldObj != "object") {
return oldObj; //直接退出
} else {
var newObj = Array.isArray(oldObj) ? [] : {}; //不要直接创建对象,先使用三目运算符判断是创建数组还是对象
for (var key in oldObj) {
newObj[key] = clone(oldObj[key]); //如果属性有内嵌子对象就再次调用克隆函数 递归的思想
}
return newObj; //返回新对象或数组
}
}
//克隆一个对象
var kelongti = clone(benti);
benti.top = "本体的头受伤了"; //本体的头受伤了
console.log(benti);
console.log(kelongti);
//参数为null
var boor = clone(null);
console.log(boor);
//参数为数字,字符串
var number = clone(12);
var string = clone("string");
console.log(number);
console.log(string);
//参数为数组
var arr = clone([1, 2, 3, 4, 5]);
console.log(arr);
输出结果
这样就实现了深克隆了,最主要的还是这句代码
newObj[key] = clone(oldObj[key]);利用了递归的思想