js 淺復(fù)制深復(fù)制

本文分三部分
1.先說什么是深復(fù)制淺復(fù)制 在定義上進(jìn)行區(qū)分
2.深復(fù)制淺復(fù)制的各個(gè)應(yīng)用場景
3.總結(jié)淺復(fù)制深復(fù)制優(yōu)缺點(diǎn)
4.如何實(shí)現(xiàn)深復(fù)制
5.json實(shí)現(xiàn)深復(fù)制的注意事項(xiàng)

一.深復(fù)制淺復(fù)制的定義概念區(qū)分

什么是淺復(fù)制?
文字定義:就是原對象里有屬性也是一個(gè)對象,當(dāng)在復(fù)制時(shí),這個(gè)對象屬性引用值沒有發(fā)生變化,還是指向原對象的對象屬性。
代碼表示:

const originObj={a:1,objAttr:{b:2}};
const newObj={...originObj};
newObj.objAttr===originObj.objAttr///比較結(jié)果是true, newObj.objAttr對象引用值沒有變化,還是指向原對象屬性

什么是深復(fù)制
文字定義:就是原對象里有屬性也是一個(gè)對象,當(dāng)在復(fù)制時(shí),這個(gè)對象屬性引用值發(fā)生變化被賦予新值,不指向原對象的對象屬性。
代碼表示:

const originObj={a:1,objAttr:{b:2}};
const newObj=JSON.parse(JSON.stringify(orginObj));
newObj.objAttr===originObj.objAttr///比較結(jié)果是false,  newObj.objAttr對象引用值變化,不指向原對象屬性

二.深復(fù)制淺復(fù)制的應(yīng)用場景

深度復(fù)制應(yīng)用場景
深復(fù)制 主要作用是隔離兩個(gè)相關(guān)變量的相互影響,有哪些場景要注意隔離影響呢
1.常規(guī)就是函數(shù)傳遞參數(shù)時(shí),隔離相互影響;函數(shù)調(diào)用要傳遞一個(gè)對象數(shù)據(jù)時(shí),養(yǎng)成深克隆后傳遞的習(xí)慣;不要希望別人沒有修改你的原對象;另外代碼會更加穩(wěn)定。多人協(xié)作寫代碼,可能此刻是好的,下一個(gè)月就有人修改了你調(diào)用的函數(shù),修改了你傳遞進(jìn)來的對象。導(dǎo)致莫名錯(cuò)誤。
如下面代碼

function mdy(obj){
obj.subObj.xx=123;//函數(shù)內(nèi)部修改了傳遞進(jìn)來的對象 如果是你是深克隆后傳遞進(jìn)來  就不用超心。
}

2.在react的hook里 如useEffect 依賴一個(gè)狀態(tài)是對象時(shí),只有對象引用發(fā)生變化,這個(gè)useEffect才會執(zhí)行

const [stateData,setStateData]=  useState({obj:{},xx:3})
useEffect(()=>{
//do something
},[ stateData.obj ] );//這個(gè)時(shí)候 stateData每次設(shè)置就必須是深克隆

在react里應(yīng)用還很多的,例如是否應(yīng)該更新渲染 的判斷 比較的也是引用是否發(fā)生了變化。另外全局狀態(tài)的追蹤記錄 也是引用比較。
在對象要進(jìn)行引用比較時(shí) 深克隆比較安全

淺復(fù)制應(yīng)用場景
1.兩種復(fù)制方式都能解決問題時(shí),就采用淺復(fù)制,因?yàn)?strong>淺復(fù)制性能更好。
什么情況下會出現(xiàn)淺復(fù)制解決問題就好
比如下面代碼

const obj={a:1,b:{c:3}}
//你關(guān)注的值只有a時(shí),不關(guān)注b時(shí),就采用淺復(fù)制。即只關(guān)心基本類型的數(shù)據(jù),不關(guān)心引用類型的數(shù)據(jù)時(shí)。采用淺復(fù)制

2.必須采用淺復(fù)制的場景
就是對象之間可以共享一個(gè)公共對象,通過共享公共對象可以節(jié)省內(nèi)存,也更好的實(shí)現(xiàn)協(xié)同。
例如一般每個(gè)學(xué)生都有一個(gè)相同的班主任。這個(gè)時(shí)候通過淺復(fù)制學(xué)生對象再生產(chǎn)出一個(gè)學(xué)生對象,兩個(gè)學(xué)生對象之間就能夠共享班主任對象了。 代碼表示如下

const stdA={age:10,name:'a' sex:boy,headTeacher:{name:"liwei",sex:'gril' }};
const stdB={...stdA};//通過淺復(fù)制出來學(xué)生b就和學(xué)生a有了相同的班主任。
stdB.age=12;
stdB.name='b'

如果有成千上萬個(gè)學(xué)生共享一個(gè)校長對象,那淺復(fù)制節(jié)約內(nèi)存的效果就很突出了。

三.總結(jié)淺復(fù)制深復(fù)制優(yōu)缺點(diǎn)比較
上面已經(jīng)對各個(gè)場景進(jìn)行了細(xì)說。
總結(jié)記憶可以是:淺復(fù)制執(zhí)行效率高,共用內(nèi)存省內(nèi)存;深復(fù)制隔離影響強(qiáng)。
所以寫代碼時(shí)你是想共用還是想隔離就要思考清楚。

四.深復(fù)制的實(shí)現(xiàn)方法

實(shí)現(xiàn)時(shí)要注意3個(gè)問題
1.對 基本類型 函數(shù)類型 引用類型的判斷
2.要寫一個(gè)深度遞歸,以及注意采用尾遞歸來優(yōu)化遞歸性能
3.要解決循環(huán)引用問題

下面是一個(gè)具體實(shí)現(xiàn)

//整體想法 對基礎(chǔ)類型 函數(shù)類型 引用類型分情況處理,用weakMap處理循環(huán)引用的情況
      const deepClone = (obj, distObj = {}, weakMap = new WeakMap()) => {
        if (!obj || typeof obj !== "object") {
          return distObj;
        }
        if (!weakMap.has(obj)) {
          weakMap.set(obj, distObj);
        }

        Object.keys(obj).forEach((key) => {
          const keyValue = obj[key];
          //函數(shù)的情況
          if (typeof keyValue === "function") {
            distObj[key] = keyValue;
            //對象情況
          } else if (keyValue && typeof keyValue === "object") {
            //說明還沒有發(fā)生循環(huán)引用
            if (!weakMap.has(keyValue)) {
              //復(fù)制引用對象
              const dt = { ...keyValue };
              distObj[key] = dt;
              //把值存入weakMap
              weakMap.set(keyValue, dt);
              deepClone(dt, dt, weakMap);
            } else {
              //說明發(fā)生了循環(huán)引用
              distObj[key] = weakMap.get(keyValue);
            }
          } else {
            //基礎(chǔ)類型情況
            distObj[key] = keyValue;
          }
        });

        return distObj;
      };

      //測試用例 構(gòu)造循環(huán)引用
      const ddd = { aa: 44, c: 3, fun: () => 0 };
      const ttt = { d: 4, g: { d: ddd } };
      ddd.t = ttt;

      console.log("克隆結(jié)果", deepClone(ttt));

淺復(fù)制實(shí)現(xiàn)的兩個(gè)方法(上面的代碼能夠?qū)崿F(xiàn)淺復(fù)制)。

const obj={a:1,c:{d:2}};
const obj2={...obj};/// es6  對象擴(kuò)展... 簡單實(shí)現(xiàn)淺復(fù)制
const obj3=Object.assign({},obj);//Object.assign 這個(gè)api實(shí)現(xiàn)淺復(fù)制

obj.c===obj2.c===obj3.c;//三個(gè)都相對

五.常用的json實(shí)現(xiàn)深復(fù)制注意事項(xiàng)
1.underfine值不能復(fù)制
2.函數(shù)不能復(fù)制
3.循環(huán)引用問題無法解決,會報(bào)異常,需要做錯(cuò)誤處理
具體可以看看別人的 http://www.lxweimin.com/p/52db1d0c1780

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容