記錄一下最近在學的深淺拷貝

前言

最近寫代碼常常用到深淺拷貝,從一開始的悶頭使用漸漸想要深究其理,這篇文章記錄一下個人認爲,有所不足,恭請指正數組

咱們能夠先看看一個常遇到的一個小問題異步

let a = {
    age:1
}
let b = a
a.age = 2
console.log(b.age)   //2

從上面的例子中咱們看到了,若是給一個變量賦值一個對象,那麼二者的值會是同一個引用,其中一方變化,另外一方也會有相應的改變。
一般咱們開發中不但願出現這樣的問題,咱們可使用淺拷貝來解決這個問題。函數

淺拷貝

首先能夠經過 Object.assign 來解決這個問題。post

let a = {
    age:1
}
let b = Object.assign({} , a)
a.age = 2
console.log(b.age)   //1

固然大多數狀況咱們比較喜歡經過展開運算符(...)來解決性能

let a = {
        age:1
    }
    let b ={...a} 
    a.age = 2
    console.log(b.age)   //1

一般淺拷貝就解決了大部分問題,可是若是遇到如下狀況就須要用到深拷貝了spa

let a = {
            age:1,
            jobs:{
            first:'FE'
            }
        }
        let b ={...a} 
        a.jobs.first = 'native'
        console.log(b.jobs.first)   //native

淺拷貝只解決了第一層的問題,若是接下去的值仍是有對象的話,那麼就又回到剛剛的開始問題了。二者享有相同的引用。要解決這個問題,咱們就要引入深拷貝。code

深拷貝

這個問題一般能夠經過 JSON.parse(JSON.stringify(object)) 來解決。對象

let a = {
                age:1,
                jobs:{
                first:'FE'
                }
            }
            let b =JSON.parse(JSON.stringify(a))
            a.jobs.first = 'native'
            console.log(b.jobs.first)   //FE

可是該方法也是有侷限性的:遞歸

  • 會忽略underfind
  • 不能序列化函數
  • 不能解決循環引發的對象

咱們再看一下下面這個狀況ip

let obj ={
 
    a:undefined,
    b:function(){},
    name:'yck'
    
   }

   let newObj = JSON.parse(JSON.stringify(obj))
   console.log(newObj) //{name : 'yck'}

你會發如今上述狀況中,該方法忽略掉函數和undefind
可是在一般狀況下,複雜數據都是可序列化的,因此這個函數能夠解決大部分問題,而且該函數是內置函數中處理深拷貝性能最快的。固然若是你的數據中含有以上三種狀況下。可使用 lodash 的深拷貝函數
若是你所須要拷貝的對含有內置類型而且不包括函數,可使用 MessageChannel

function structuralClone(obj){
      return new Promise(resolve => {
          const {port1,port2} = new MessageChannel();
          port2.onmessage = ev =>resolve(ev.data);
          port1.postMessage(obj)
      })
  }
  var obj = {a:1,b:{
      c:b
  }}
  //注意這個方法是異步的
  //能夠處理undefined和循環引用對象
  const clone = await structuralClone(obj);

深拷貝與淺拷貝的區別

深拷貝和淺拷貝最根本的區別在因而否是真正獲取了一個對象的複製實體,而不是引用。

淺拷貝—-只是拷貝了基本類型的數據,而引用類型數據,拷貝後也是會發生引用,咱們把這種拷貝叫作「(淺複製)淺拷貝」,換句話說,淺拷貝僅僅是指向被拷貝的內存地址,若是原地址中對象被改變了,那麼淺拷貝出來的對象也會相應改變。

深拷貝—-在計算機中開闢了一塊新的內存地址用於存放複製的對象。

淺拷貝實例
//此遞歸方法不包含數組對象

var obj = { a:1, arr: [2,3] };
var shallowObj = shallowCopy(obj);

function shallowCopy(src) {
  var newobj = {};
  for (var prop in src) {
    if (src.hasOwnProperty(prop)) {
      newobj[prop] = src[prop];
    }
  }
  return newobj;

由於淺複製只會將對象的各個屬性進行依次複製,並不會進行遞歸複製,而 JavaScript 存儲對象都是存地址的,因此淺複製會致使 obj.arr 和 shallowObj.arr 指向同一塊內存地址,大概的示意圖以下。

clipboard.png

致使的結果就是:

shallowObj.arr[1] = 5;
console.log(obj.arr[1]);   //  5

深拷貝實例
而深複製則不一樣,它不只將原對象的各個屬性逐個複製出去,並且將原對象各個屬性所包含的對象也依次採用深複製的方法遞歸複製到新對象上。這就不會存在上面 obj 和 shallowObj 的 arr 屬性指向同一個對象的問題。

var obj = { 
    a:1, 
    arr: [1,2],
    nation : '中國',
    birthplaces:['北京','上海','廣州']
};
var obj2 = {name:'楊'};
obj2 = deepCopy(obj,obj2);
console.log(obj2);
//深複製,要想達到深複製就須要用遞歸
function deepCopy(o,c){
   var c = c || {};
   for(var i in o){
   if(typeof o[i] === 'object'){
          //要考慮深複製問題了
          if(o[i].constructor === Array){
            //這是數組
            c[i] =[];
          }else{
            //這是對象
            c[i] = {};
          }
          deepCopy(o[i],c[i]);
        }else{
          c[i] = o[i];
        }
     }
     return c
 }

結果以下面的示意圖所示:

clipboard.png

這樣obj和obj2分別擁有不一樣的內存地址,兩邊值的改變互不影響。

深淺拷貝也可使用JQuery的extend方法,注意使用合併返回值

let newObj = $.extend(true,{},partcontent);
相關文章
相關標籤/搜索