1. 程式人生 > 前端設計 >javascript的深拷貝和淺拷貝

javascript的深拷貝和淺拷貝


 這個面試題屬於比較常見的面試題,但是我們在回答過程中但是還是有很多細節我們沒有回答出來,那麼接我們一起來探討一下這個問題 


為什麼會出現深拷貝淺拷貝?

      由於 javascript 儲存的方式是堆疊儲存,在 javascript 中分為兩類資料型別,一類是簡單資料型別,一類是複雜資料型別


    簡單資料型別:

  • String
  • Number
  • Boolean
  • undefined
  • null
  • Symbol


     簡單資料型別的儲存方式

// 首先簡單資料型別儲存來棧中
let a = 1;
let b = a;
    b = 2
// 在儲存的時候發生了什麼事情複製程式碼



解析:

  1. 申明變數 a 並且賦值 1 ,這個時候由於 a 儲存的是簡單資料型別,所以在棧中開闢空間進行儲存
  2. a 賦值給 b 的過程中,其實是將 a 的值賦值給 b ,a 屬於簡單資料類,那麼,就在棧中新開闢空間進行儲存
  3. 這個時候給 b 重新賦值的時候,a 並不會受到影響,還是原來的值


複雜資料型別:

  • Object
  • Array
  • Function
  • Date
  • RegExp
  • ...

複雜資料型別儲存方式

let person1 = {
    name:"張三"
}
let person2 = person1
    person2.name = "李四"

console.log(person1.name) // 李四
複製程式碼



解析:

  1. 申明 person1 物件,但是 person1 的內容存在堆中,棧中存著指向這個堆的地址
  2. 這個時候在對 person2 進行賦值的時候,賦值的不是內容,而是棧中的地址,這個時候 person1 和 person2 都指向一個內容
  3. 在對 person2 的值進行更改的是,其實是通過地址找到了內容進行更改地址的內容
  4. 那麼 內容都改變了,這個時候列印 person1 的時候,就會發現值也改變了
  5. 這種現象就叫做淺拷貝,淺拷貝的危害是會汙染到源資料


問題:出現淺拷貝會汙染到資料來源,在做專案的過程中,經常會出現莫名奇妙的錯誤,很多情況下就是因為淺拷貝的問題,所以淺拷貝出現,我們需要進行深拷貝,深拷貝說白了,就是在堆中重新開闢空間去儲存內容,那麼現在有哪些深拷貝的方式呢?


深拷貝的方式有哪些?


1、JSON.parse(JSON.stringify())

let person1 = {
    name:"張三"
}
let person2 = JSON.parse(JSON.stringify(person1))
    person2.name = "李四"
console.log(person1.name) // 張三複製程式碼

看似好像可以實現深拷貝,源資料沒有更改,但是 JSON.parse(JSON.stringify())有它自身的問題

 
   let obj = {        
        name:'張三',age:undefined,fn:function(){            
            console.log(1)        
        },data:new Date(),reg:/^\d$/,person:{            
            name:'王五'        
        }    
    }
let obj1 = JSON.parse(JSON.stringify(obj))    
obj1.person.name = "李四"    
console.log(obj1,obj)

複製程式碼


解析:我們發現確實是實現了深拷貝,源資料的 "王五" 並沒有更改,但是也暴露了問題

問題:

  1. 拷貝之後的 data 源資料是物件,但是拷貝之後變成了字串
  2. 函式無法拷貝
  3. 正則無法拷貝
  4. undefined 無法拷貝


2、Object.assign()

let person1 = {
    name:"張三"
}
let person2 = {}
    Object.assign(person2,person1)
    person2.name = "李四"
console.log(person1.name) // 張三

複製程式碼

看似好像可以實現深拷貝,源資料沒有更改,但是 Object.assign()也有它的問題

    let obj = {        
        name:'張三',perosn:{            
            name:'王五',obj:{                
                age:15            
            }       
        }    
    }    
    let obj1 = {}    
    Object.assign(obj1,obj)    
    obj1.perosn.name = "李四"    
    console.log(obj1,obj)複製程式碼


解析:Object.assign能實現拷貝,但是問題和優勢很明顯

問題:

  1. 第一層可以實現深拷貝,但是二層一下的物件無法實現深拷貝,我們看到,在更改person.name 的值的時候,發現源資料也發生的更改

優勢:

  1. 可以實現 undefined 的拷貝
  2. 可以實現對函式的拷貝
  3. 可以實現對 Date 物件的拷貝
  4. 可以實現正則的拷貝

跟Object.assign有著一樣的問題的還有擴充套件運算子


3、擴充套件運算子

    let obj = {        
       name:'張三',fn:function(){            
          console.log(1)        
       },perosn:{            
           name:'王五',obj:{                
              age:15            
           }        
       }    
     }
 let obj1 = {...obj} 
obj1.perosn.name = '李四' 
console.log(obj1,obj)
複製程式碼


解析:它的結果跟 Object.assign 一樣


4、MessageChannle 管道

  let obj = {        
        name:'張三',// fn:function(){        
        //     console.log(1)        
        // },
        data:new Date(),obj:{                
                age:15            
            }        
        }    
    }
// 兩個物件 prot1 port2  new MessageChannle()    
// 處理非同步的問題 promise : 非同步中的微任務    
// resolve:成功的回撥 reject:失敗的回撥    
// port1 ======== port2    
function deepCopy(obj){        
       return new Promise(resolve=>{            
                    let {port1,port2} = new MessageChannel()            
                    port2.onmessage = ev => resolve(ev.data)             
                    port1.postMessage(obj)        
        })    
}    
deepCopy(obj).then(res=>{        
    let obj1 = res             
    obj1.name = "李四"        
    obj1.perosn.name = "小五"        
    console.log(obj1,obj)    
}).catch(err=>{        
    console.log(err)    
})
複製程式碼


解析:MessageChannle 可以實現深拷貝,並且可以實現其他資料的拷貝

優勢:

  1. 可以實現 undefined 的拷貝
  2. 可以實現對函式的拷貝
  3. 可以實現對 Date 物件的拷貝
  4. 可以實現正則的拷貝

問題:

  1. 對函式不能實現拷貝,拷貝函式就會報錯


5、自己簡單實現遞迴拷貝

function deepCopy(obj){        
    let newObj = null        
    if(typeof obj ==='object'&&obj !== null){                     
      newObj = obj instanceof Array?[]:{}            
      for(var i in obj){                
          newObj[i] = deepCopy(obj[i])             
      }        
    }else{            
         newObj = obj // 最終會走這一步        
     }        
         return newObj    
}複製程式碼

解釋:當然這個拷貝是最簡單的,也存在一些問題,但是實現遞迴最簡單的方式就是這個,很多情況下我們為了保證不會出問題,都會用第三方工具庫  lodash ,這樣寫出來才更加保險一點


最後:本人喜歡研究面試題,希望有更多志同道合的朋友一起來交流研究,可以幫助修改簡歷