1. 程式人生 > >js對象深拷貝淺拷貝

js對象深拷貝淺拷貝

一次 aso type res create pro bject 數據結構 存在

對象的深拷貝於淺拷貝

對於基本類型,淺拷貝過程就是對值的復制,這個過程會開辟出一個新的內存空間,將值復制到新的內存空間。而對於引用類型來書,淺拷貝過程就是對指針的復制,這個過程並沒有開辟新的堆內存空間,只是將指向該內存的地址進行了復制。然而對引用類型的淺拷貝會出現一個問題,那就是修改其中一個對象的屬性,則另一個對象的屬性也會改變。產生了問題那必然有相對解決的方法, 就這樣深拷貝就開始入場了,深拷貝會開辟新的棧,兩個對象對應兩個不同的地址,這樣一來,改一個對象的屬性,也不會改變另一個對象的屬性。
下面我們用代碼看一下深拷貝於淺拷貝是如何實現的 :

對象的淺拷貝

對象的淺拷貝實現方法很簡單,說白了就是一個for in 循環的事情 :

let obj = {
    nNum : 1,
    sName : 'aaron'
};

function shallowCopy(args) {

    let result = {};
    
    for(let item in args) {
        if(args.hasOwnProperty(item)) {
            result[item] = args[item];
        };
    };

    return result;
};

shallowCopy(obj);

或者,我們可以使用最常見的jQuery來實現這個功能 :

$.extend({} , obj);

亦或者,我們也可以采用ES6的方法用一行代碼實現相同的功能 :

Object.assign({} , obj);

對於淺拷貝就說到這了,如果有補充或者發現問題的大牛可以回復我或者郵件通知,我會第一時間進行回復,好了廢話說多了,我們接下來看一下深拷貝

對象的深拷貝

如果我們把上面的obj稍微修改一下那淺拷貝就有心無力了 :

let obj = {
    nNum : 1,
    oAar : [1,2,3,4]
};

我們不妨可以再用上面的方法試一下

let newObj = shallowCopy(obj);

newObj === obj      // false
newObj.oAar === obj.oAar   // true

從上面的結果顯示我們可以看出,newObj.oAar與obj.oAar指針所指的是同一個位置,那我們如何改正這個問題呢?

其實也很容易,我們將obj.oAar再看待為一個新的對象,對其再進行一次淺拷貝不就可以了嗎??不說了,上代碼

function deepCopy(args) {

    let result = {};
    
    for(let item in args){
         if(typeof args[item] === 'object' && args[item] !== null){
             result[item] = deepCopy(args[item]);
         }else{
             result[item] = args[item];
         };
     };

    return result;
};

result(obj);

我們來驗證一下

let newObj = result(obj);

newObj === obj      // false
newObj.oAar === obj.oAar   // false

結果也顯示我們實現了,ok

但是有沒有其他的方式呢,比如說更簡潔一些的,也是有的:

我們可以使用jQuery的$.extend(true, {}, obj),同樣也可以實現,是不是覺得眼熟。前面我們說淺拷貝的時候也說過這個方法,但是多了一個為true的參數,$.extend()方法,第一個參數默認為false,當設置它為true時則會開啟深拷貝模式。

想想還有沒有什麽其他的方式= ̄ω ̄=?

其實還有一個捷徑可走

還是上面舉的那個obj,我們對它再進行一次嘗試

 let obj = {
    nNum : 1,
    oAar : [1,2,3,4]
 };
 
 function deepCopy(args) {
      let result = {};
      
      try {
          result = JSON.parse(JSON.stringify(args));
      }
      
      return result;
  }
 
 let newObj = deepCopy(obj);
 
 newObj === obj      // false
 newObj.oAar === obj.oAar   // false

ヾ(?`Д′?) 什麽鬼,這樣也行? 那還前面說那麽多廢話,直接上這個方法豈不是爽歪歪?

但是這裏不得不說明一點,投機取巧往往能達到看似相同的結果,但是還是存在一些隱性的問題的

就如上面這種方式和遞歸的方法相比是有弊端的,比如它會拋棄對象的constructor。也就是深拷貝之後,不管這個對象原來的構造函數是什麽,在深拷貝之後都會變成Object。

這種方法能正確處理的對象只有 Number, String, Boolean, Array, 扁平對象,即那些能夠被 json 直接表示的數據結構。RegExp對象是無法通過這種方式深拷貝。

最後再說一種方法,可以使用Object.create()方法達到深拷貝的效果

function deepCopy(args) {
    let result = {};
    
    for(let item in args){
           if(typeof args[item] === 'object' && args[item] !== null){
               result[item] = args[item].constructor === Array ? [] :
               Object.create(prop);
           }else{
               result[item] = args[item];
           };
     };
    
    return result;
}

let newObj = deepCopy(obj);
 
newObj === obj      // false
newObj.oAar === obj.oAar   // false

好了,更多的第三方庫的方法我們就不一一列舉了,原理更重要嘛,對吧。

最後我們來總結一下:

淺拷貝我們說了3種方法 :

  • 通過for...in循環來實現
  • 通過Object.assign()方法來實現
  • 通過jQuery中的$.extend()方法來實現

深拷貝我們同樣說了四種方法 :

  • 通過遞歸的手法實現
  • 通過jQuery中的$.extend()添加true參數方法來實現
  • 通過JSON序列化反序列化來實現
  • 用過Object.create()方法來實現

除了這幾種如果各位還想到了其他什麽好的方法歡迎下方留言ヾ( ̄▽ ̄)

js對象深拷貝淺拷貝