深拷贝和浅拷贝的理解
1. 区分基本数据类型和引用类型
基本数据类型:直接存储在栈 (stack) 中的数据 包括:String, Number, Boolean, Null, Undefined,Symbol
引用类型:栈中存引用,真实的数据存储在堆中。包括:Object 、Array 、Function 、Data
2. 对比两个对象/数组是否相等
let obj1 = {
name: "lucy",
age: 20,
};
let obj2 = {
name: "lucy",
age: 20,
};
console.log(obj1 === obj2); // false
console.log(JSON.stringify(obj1) === JSON.stringify(obj2)); // true
//-----------------------------------------------------------------
let arr1 = ["nihao", "zaijian"];
let arr2 = ["nihao", "zaijian"];
console.log(arr1 === arr2); // false
console.log(JSON.stringify(arr1) === JSON.stringify(arr2)); // true
原因在于对象和数组是引用类型,‘==’以及‘===’比较的是引用地址,两个对象虽然数据内容相同,但是引用地址不同,所以输出false, 转换为JSON字符串形式后,输出为true。
let obj1 = {
name: "lucy",
age: 20,
};
let obj2 = obj1; //obj2和obj1指向同一个引用地址
console.log(obj1 === obj2); // true
3. 深拷贝
首先引入一个问题,还是上面的代码
let obj1 = {
name: "lucy",
age: 20,
};
let obj2 = obj1; //obj2和obj1指向同一个引用地址
obj2.name = "peter";
console.dir(obj1);
console.dir(obj2);
输出结果:
原因很简单,引用两个对象指向同一个地址,所以他们‘共用’相同的数据,当某一方修改了数据,另一方自然会受到影响。
那如果我们的需求是一方改变了引用类型中的某一个属性,另一方不受到影响呢?这时候就需要引入深拷贝了。
深拷贝:在进行赋值之前,为引用类型的数据成员另辟了一个独立的内存空间,实现真正内容上的拷贝 。这种拷贝称为深拷贝。简单来说,就是重新开辟一个堆空间,专门存储另一个对象的数据,这样两个对象的数据就各自独立,不会互相影响。
3.1 深拷贝的方法1——JSON序列化
let obj1 = {
name: "lucy",
age: 20,
hobby: {
sport: "basketball",
},
beFriend() {},
};
function deepClone(obj1) {
//构造一个新的对象
let obj2 = JSON.parse(JSON.stringify(obj1));
obj2.name = "jensen";
obj2.hobby.sport = "football";
return obj2;
}
console.log(deepClone(obj1));
console.log(obj1);
输出结果:
可以发现这样就实现了深拷贝,但是存在一个问题是原对象中的函数方法没有被拷贝。
3.2 深拷贝的方法2——遍历
遍历是更为常用的一种方法。
这里我们采用for in 方法进行遍历。
let obj1 = {
name: "lucy",
age: 20,
hobby: {
sport: "basketball",
},
beFriend() {},
};
function deepClone(obj1) {
// 如果typeof obj1.splice是function,那么obj1是数组类型, 否则是对象类型
let result = typeof obj1.splice === "function" ? [] : {};
if (typeof result == "object") {
for (let el in obj1) {
if (typeof obj1[el] == "object") {
result[el] = deepClone(obj1[el]);
} else {
result[el] = obj1[el];
}
}
return result;
}
return obj1;
}
function objClone(obj) {
let obj2 = deepClone(obj1);
obj2.name = "ezreal";
obj2.hobby.sport = "run";
return obj2;
}
console.log(objClone(obj1));
console.log(obj1);
输出结果:
由此,我们利用for in 遍历 + 递归实现了一个较为完善的深拷贝。
3.3 深拷贝的方法3 — lodash库
引入一个lodash库,简单调用api即可。
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
<script>
let obj1 = {
name: "lucy",
age: 20,
hobby: {
sport: "basketball",
},
beFriend() {},
};
function dC(obj1) {
let obj2 = _.cloneDeep(obj1);
obj2.name = "sam";
obj2.hobby.sport = "swim";
return obj2;
}
console.log(dC(obj1));
console.log(obj1);
</script>
输出结果:
4. 浅拷贝
对比深拷贝即可, 浅拷贝就是只拷贝第一层引用。
4.1 浅拷贝方法1 —— 直接引用
let arr1 = ["nihao", { name: "lucy" }];
let arr2 = arr1;
arr
arr2[1].name = "faker";
console.table(arr1);
console.table(arr2);
输出结果:
4. 浅拷贝的方法2 —— 遍历
let arr1 = ["nihao", { name: "lucy" }];
function shallowCopy(arr1) {
let result = typeof arr1.splice === "function" ? [] : {};
if (typeof arr1 === "object") {
for (let el in arr1) {
result[el] = arr1[el];
}
return result;
}
return arr1;
}
function arrCopy(arr1) {
let arr2 = shallowCopy(arr1);
arr2[0] = "zaijian";
arr2[1].name = "dade";
return arr2;
}
console.table(arrCopy(arr1));
console.table(arr1);
输出结果:
4. 浅拷贝的方法3 —— Object.assign
let arr1 = ["nihao", { name: "lucy" }];
function arrCopy(arr1) {
let arr2 = Object.assign(arr1, []);
arr2[1].name = "ajax";
return arr2;
}
console.table(arrCopy(arr1));
console.table(arr1);
输出结果: