關於js中的深淺拷貝和assign到底是深拷貝還是淺拷貝的爭論

YYX發表於2018-03-06

     又到了跳槽的熱門期,有很多小夥伴都問深淺克隆的問題,我面試時也問了很多關於深淺克隆和assign的問題,但能清楚的答上來的很少,還有一些做了2,3年前端對堆疊也分不清,下面是我自己的理解和一些經驗。

       要理解深淺克隆首先要知道js的資料型別:字串(String)、數字(Number)、布林(Boolean)、陣列(Array)、物件(Object)、空(Null)、未定義(Undefined)以及es6新增資料型別symbol。

js將這些資料型別分為兩類:基本型別和引用型別。其中基本資料型別指的是簡單的資料段,包括:字串(String)、數字(Number)、布林(Boolean)、空(Null)、未定義(Undefined)。引用據型別指的是有多個值構成的物件,包括:陣列(Array)、物件(Object)。js為什麼會把他們分為兩種型別,根本上是因為基本資料型別儲存在棧記憶體,儲存在棧記憶體的必須是大小固定的資料,而引用型別儲存在堆記憶體中,引用型別的大小不固定,只能儲存在堆記憶體中,然後把它的地址放在棧中訪問。

舉個例子:

var age = 27;
var worker = {
       name:"yyx"
          } 複製程式碼

關於js中的深淺拷貝和assign到底是深拷貝還是淺拷貝的爭論

所以操作的是堆還是棧就出現了深淺拷貝的問題

深淺拷貝

例:
var age = 27;
var newAge = age;
console.log(age)//------27
console.log(newAge)//---27
newAge = 72;
console.log(age)//------27
console.log(newAge)//---72

當操作的是基本型別時newAge並沒有對age產生影響

例:
var worker = {
    name:"yyx"
  }
  var newWorker = worker
  console.log(worker)//-------{name:"yyx"}
  console.log(newWorker)//----{name:"yyx"}
  newWorker.name = "yu"
  console.log(worker)//-------{name:"yu"}
  console.log(newWorker)//----{name:"yu"}
當操作的是引用型別時,改變newWorker的值時worker的值也發生變化了複製程式碼

因為上面兩個例子克隆的都只是棧,而克隆的引用型別還指向同一個堆所以改變其中一個的資料,別外的也會發生改變,這就是淺克隆,如圖:

關於js中的深淺拷貝和assign到底是深拷貝還是淺拷貝的爭論

而想要完成深克隆,即worker和newWorker互不影響,最常用的就是方法就是遞迴:

function deepClone(obj) {
  if (typeof obj != "object") {
    return obj
  }
  var newObj = {};
  for (var i in obj) {
    newObj[i] = deepClone(obj[i]);
  }
  return newObj
}
var worker = {
    name:"yyx",
    age:"27"
}
var newWorker = deepClone(worker)
console.log(worker)//----{name:"yyx", age:"27"}
console.log(worker)//----{name:"yyx", age:"27"}
newWorker.name = "yu"
console.log(worker)//----{name:"yyx", age:"27"}
console.log(newWorker)//-{name:"yu", age:"27"}
此時改變newWorker的值時就不會對worker產生影響複製程式碼

此時newWorker指向了新的堆,更改時不會對worker產生影響

如圖:

關於js中的深淺拷貝和assign到底是深拷貝還是淺拷貝的爭論

此外還有別的方法實現深克隆var obj1 = JSON.parse(JSON.stringify(obj))這幾種都是常用的物件深克隆方法

ES6新增方法assign()

assign(obj,...obj)方法接受多個引數,第一個引數為拷貝目標,剩餘引數是拷貝源。此方法可以將...obj中的屬性複製到obj中,名字相同的屬性會覆蓋,實現了物件拷貝

有意思的是assign對只有1層的物件實現的是深拷貝,多層物件實現的是淺拷貝

 

例1:
var worker = {
  name:"yyx"
 }
 var newWorker = Object.assign({},worker)
 newWorker.name = "yu"
 console.log(worker)//----{name:"yyx"}
 console.log(newWorker)//-{name:"yu"}
新的物件newWorker並沒有對worker產生影響

例2:
var worker = {
  name:"yyx",
  info:{
    age:27
  }
 }
 var newWorker = Object.assign({},worker)
 newWorker.info.age = 20
 console.log(worker.info.age)//----20
 console.log(newWorker.info.age)//-20
新的物件newWorker改變影響到了worker

複製程式碼

由上面例子可以看出來,assign的坑,這也是面試時面試官為什麼都喜歡問assign是深克隆還是淺克隆的原因


相關文章