如何區(qū)分深拷貝與淺拷貝,簡單點(diǎn)來說,就是假設(shè)B復(fù)制了A,當(dāng)修改A時(shí),看B是否會發(fā)生變化,如果B也跟著變了,說明這是淺拷貝,拿人手短,如果B沒變,那就是深拷貝,自食其力。
1. 如果是基本數(shù)據(jù)類型,名字和值都會儲存在棧內(nèi)存中
var a = 1;
b = a; // 棧內(nèi)存會開辟一個(gè)新的內(nèi)存空間,此時(shí)b和a都是相互獨(dú)立的
b = 2;
console.log(a); // 1
當(dāng)然,這也算不上深拷貝,因?yàn)樯羁截惐旧碇会槍^為復(fù)雜的object類型數(shù)據(jù)。
2. 如果是引用數(shù)據(jù)類型,名字存在棧內(nèi)存中,值存在堆內(nèi)存中,但是棧內(nèi)存會提供一個(gè)引用的地址指向堆內(nèi)存中的值
比如淺拷貝:

image.png
當(dāng)b=a進(jìn)行拷貝時(shí),其實(shí)復(fù)制的是a的引用地址,而并非堆里面的值。

image.png
而當(dāng)我們a[0]=1時(shí)進(jìn)行數(shù)組修改時(shí),由于a與b指向的是同一個(gè)地址,所以自然b也受了影響,這就是所謂的淺拷貝了。

image.png
那,要是在堆內(nèi)存中也開辟一個(gè)新的內(nèi)存專門為b存放值,就像基本類型那樣,豈不就達(dá)到深拷貝的效果了

image.png
3. 實(shí)現(xiàn)淺拷貝的方法
(1)for···in只循環(huán)第一層
// 只復(fù)制第一層的淺拷貝
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
(2)Object.assign方法
var obj = {
a: 1,
b: 2
}
var obj1 = Object.assign(obj);
obj1.a = 3;
console.log(obj.a) // 3
(3)直接用=賦值
let a=[0,1,2,3,4],
b=a;
console.log(a===b);
a[0]=1;
console.log(a,b);

image.png
4. 實(shí)現(xiàn)深拷貝的方法
(1)采用遞歸去拷貝所有層級屬性
function deepClone(obj){
let objClone = Array.isArray(obj)?[]:{};
if(obj && typeof obj==="object"){
for(key in obj){
if(obj.hasOwnProperty(key)){
//判斷ojb子元素是否為對象,如果是,遞歸復(fù)制
if(obj[key]&&typeof obj[key] ==="object"){
objClone[key] = deepClone(obj[key]);
}else{
//如果不是,簡單復(fù)制
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
let a=[1,2,3,4],
b=deepClone(a);
a[0]=2;
console.log(a,b);
結(jié)果:

image.png
(2) 通過JSON對象來實(shí)現(xiàn)深拷貝
function deepClone2(obj) {
var _obj = JSON.stringify(obj),
objClone = JSON.parse(_obj);
return objClone;
}
缺點(diǎn): 無法實(shí)現(xiàn)對對象中方法的深拷貝,會顯示為undefined
(3)通過jQuery的extend方法實(shí)現(xiàn)深拷貝
var array = [1,2,3,4];
var newArray = $.extend(true,[],array); // true為深拷貝,false為淺拷貝
(4)lodash函數(shù)庫實(shí)現(xiàn)深拷貝
let result = _.cloneDeep(test)
(5)Reflect法
// 代理法
function deepClone(obj) {
if (!isObject(obj)) {
throw new Error('obj 不是一個(gè)對象!')
}
let isArray = Array.isArray(obj)
let cloneObj = isArray ? [...obj] : { ...obj }
Reflect.ownKeys(cloneObj).forEach(key => {
cloneObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
})
return cloneObj
}
(6)手動實(shí)現(xiàn)深拷貝
let obj1 = {
a: 1,
b: 2
}
let obj2 = {
a: obj1.a,
b: obj1.b
}
obj2.a = 3;
alert(obj1.a); // 1
alert(obj2.a); // 3
(7)如果對象的value是基本類型的話,也可以用Object.assign來實(shí)現(xiàn)深拷貝,但是要把它賦值給一個(gè)空對象
var obj = {
a: 1,
b: 2
}
var obj1 = Object.assign({}, obj); // obj賦值給一個(gè)空{(diào)}
obj1.a = 3;
console.log(obj.a);// 1

image.png
(8)用slice實(shí)現(xiàn)對數(shù)組的深拷貝
// 當(dāng)數(shù)組里面的值是基本數(shù)據(jù)類型,比如String,Number,Boolean時(shí),屬于深拷貝
// 當(dāng)數(shù)組里面的值是引用數(shù)據(jù)類型,比如Object,Array時(shí),屬于淺拷貝
var arr1 = ["1","2","3"];
var arr2 = arr1.slice(0);
arr2[1] = "9";
console.log("數(shù)組的原始值:" + arr1 );
console.log("數(shù)組的新值:" + arr2 );

image.png
(9)用concat實(shí)現(xiàn)對數(shù)組的深拷貝
// 當(dāng)數(shù)組里面的值是基本數(shù)據(jù)類型,比如String,Number,Boolean時(shí),屬于深拷貝
var arr1 = ["1","2","3"];
var arr2 = arr1.concat();
arr2[1] = "9";
console.log("數(shù)組的原始值:" + arr1 );
console.log("數(shù)組的新值:" + arr2 );
// 當(dāng)數(shù)組里面的值是引用數(shù)據(jù)類型,比如Object,Array時(shí),屬于淺拷貝
var arr1 = [{a:1},{b:2},{c:3}];
var arr2 = arr1.concat();
arr2[0].a = "9";
console.log("數(shù)組的原始值:" + arr1[0].a ); // 數(shù)組的原始值:9
console.log("數(shù)組的新值:" + arr2[0].a ); // 數(shù)組的新值:9

image.png
(10)直接使用var newObj = Object.create(oldObj),可以達(dá)到深拷貝的效果。
function deepClone(initalObj, finalObj) {
var obj = finalObj || {};
for (var i in initalObj) {
var prop = initalObj[i]; // 避免相互引用對象導(dǎo)致死循環(huán),如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;
}
(11)使用擴(kuò)展運(yùn)算符實(shí)現(xiàn)深拷貝
// 當(dāng)value是基本數(shù)據(jù)類型,比如String,Number,Boolean時(shí),是可以使用拓展運(yùn)算符進(jìn)行深拷貝的
// 當(dāng)value是引用類型的值,比如Object,Array,引用類型進(jìn)行深拷貝也只是拷貝了引用地址,所以屬于淺拷貝
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米" }