区分
如何区分浅拷贝与深拷贝,简单点来说,就是假设B复制了A,当修改其中一方时,看另一方是否会发生变化:
- 如果另一方跟着变了,说明这是浅拷贝,荣辱与共
- 如果另一方没变,那就是深拷贝,自立门户
原理
想真正理解和运用克隆的话,首先要了解JS中的原始值和应用值,以及它们的储存位置及方式。
原始值存储在栈里,而且存储的是变量的实际值。引用值存储在堆里,且存储的是一个指针,该指针指向内存中的某个位置,该位置存储变量的实际值。
- 基本数据类型属于原始值,名字和值都会储存在栈内存中:
var a = 1;
b = a; // 栈内存会开辟一个新的内存空间,此时b和a都是相互独立的
b = 2;
console.log(a); // 1
基本数据类型赋值算不上是深拷贝,因为深拷贝本身只针对较为复杂的Object类型数据。
- 引用数据类型,名字存在栈内存中,值存在堆内存中,但是栈内存会提供一个引用的地址指向堆内存中的值。比如浅拷贝:
当b = a
进行拷贝时,其实复制的是a的引用地址,而并非堆里面的值。
当a[0] = 1
时进行数组修改时,由于a与b指向的是同一个地址,所以自然b也受了影响,这就是所谓的浅拷贝。
那要是在堆内存中也开辟一个新的内存专门为b存放值,就像基本类型那样,就实现了深拷贝:
实现浅拷贝的方法
- 直接用
=
赋值
let a = [0,1,2,3,4],
b = a;
console.log(a === b);//true
a[0] = 1;
console.log(a,b);//[1,1,2,3,4] [1,1,2,3,4]
-
for···in
只循环第一层
// 只复制第一层的浅拷贝
function simpleCopy(obj1) {
var obj2 = Array.isArray(obj1) ? [] : {};
for (let i in obj1) {
obj2[i] = obj1[i];
}
return obj2;
}
var obj1 = {
a: 1,
b: 2,
c: {
d: 3
}
}
var obj2 = simpleCopy(obj1);
obj2.a = 3;
obj2.c.d = 4;
alert(obj1.a); // 1
alert(obj2.a); // 3
alert(obj1.c.d); // 4
alert(obj2.c.d); // 4
-
Object.assign
方法
var obj = {
a: 1,
b: 2
}
var obj1 = Object.assign({},obj);
boj1.a = 3;
console.log(obj.a) // 3
实现深拷贝的方法
- 采用递归去拷贝所有层级属性
function deepClone(obj){
let objClone = Array.isArray(obj)?[]:{};
if(obj && typeof obj==="object"){
for(key in obj){
if(obj.hasOwnProperty(key)){
//判断ojb子元素是否为对象,如果是,递归复制
if(obj[key]&&typeof obj[key] ==="object"){
objClone[key] = deepClone(obj[key]);
}else{
//如果不是,简单复制
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
let a=[1,2,3,4],
b=deepClone(a);
a[0]=2;
console.log(a,b);
- 通过JSON对象来实现深拷贝
function deepClone2(obj) {
var _obj = JSON.stringify(obj),
objClone = JSON.parse(_obj);
return objClone;
}
缺点: 无法实现对对象中方法的深拷贝,会显示为undefined
- 直接使用
var newObj = Object.create(oldObj)
,可以达到深拷贝的效果
function deepClone(initalObj, finalObj) {
var obj = finalObj || {};
for (var i in initalObj) {
var prop = initalObj[i]; // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
if(prop === obj) {
continue;
}
if (typeof prop === 'object') {
obj[i] = (prop.constructor === Array) ? [] : Object.create(prop);
} else {
obj[i] = prop;
}
}
return obj;
}
4.使用扩展运算符实现深拷贝
// 当对象中的value是基本数据类型,比如String,Number,Boolean时,是可以使用拓展运算符进行深拷贝的
// 当对象中的value是引用类型的值,比如Object,Array,引用类型进行深拷贝也只是拷贝了引用地址,所以属于浅拷贝
var car = {brand: "BMW", price: "380000", length: "5米"}
var car1 = { ...car, price: "500000" }
console.log(car1); // { brand: "BMW", price: "500000", length: "5米" }
console.log(car); // { brand: "BMW", price: "380000", length: "5米" }
参考自:
https://www.jianshu.com/p/1c142ec2ca45
https://blog.csdn.net/qq_22896159/article/details/81949296