js淺拷貝與深拷貝的區(qū)別和實(shí)現(xiàn)方式

如何區(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米" }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容