javascript對象-深淺拷貝

拷貝

談起拷貝,在咱們實際的開發應用中很是多,對於基本類型而言,在變量進行賦值的時候,會自動爲其開闢一個新的變量存儲空間,而對於複雜數據類型而言(對象),賦值操做則須要思考更多;es6

對象拷貝方式

對象的拷貝分爲深拷貝淺拷貝,其主要的深和淺,主要是對象中包含的對象類型;以下所示,對obj的拷貝區分主要在offer中;數組

var obj = {
   name:'mfy',
   age:18,
   offer:{
     company:'~',
     jobs:'sercert'
   }
}
複製代碼

淺拷貝

淺拷貝主要解決對象中引用的問題; 淺拷貝是按位拷貝對象,他會建立一個新對象,這個對象有着原始對象屬性值的一份精確拷貝。markdown

  • 若是屬性基本類型,拷貝的基本類型的值;
  • 若是屬性是內存地址(引用類型),拷貝的就是內存地址

所以若是其中一個對象改變了這個地址,就會影響到另外一個對象。即默認拷貝構造函數只是對對象進行淺拷貝複製(逐個成員一次拷貝),即只複製對象空間而不復制資源。函數

var a = {
  age:22,
  name:'mfy',
  data:{
    bane:'33',
    defa:'333,
  }
}
//b對a進行淺拷貝後
var b = a;
複製代碼
圖1 b進行賦值後b和a的引用

1.Object.assign()

Object.assgin()方法能夠把任意多個的原對象自身的可枚舉屬性拷貝給目標對象,而後返回目標對象。可是Object.assgin()進行的是淺拷貝,拷貝的是對象的屬性的引用,而不是對象自己。ui

var a ={
  age:22,
  data:{
    name:'3333',
  }
}
var b = Object.assign({},a)
b.data.name =3;
b.age =333
console.log(b)
console.log(a)
複製代碼
圖2 $Object.assgin() 修改變量對象屬性值的變化

當咱們修改經過Object.assgin()的方法建立的對象的內部屬性值的時候,對象中的基本類型是新開闢的空間存儲,而複雜的類型即對象類型,則仍是一個直接引用的關係,修改b會影響到aspa

2.Object.create(obj)

首先咱們要清楚的是Object.create()的方法使用prototype

傳參

  • 只有一個參數的時候
var obj = Object.create({a:3})
console.log(obj)
var obj1 = Object.create(null)
console.log(obj1)
複製代碼
圖3 Object.create傳遞一個參數生成的變量
  • 傳遞兩個參數的時候
var obj2 = Object.create({a:3},{ 
  foo: {
    writable: true,
    configurable: true,
    value: 'hello'
  },
})
複製代碼
圖4 Object.create傳遞兩個參數生成的變量

其實經過咱們傳遞一個參數的時候,引用類型所在的位置進行判斷,在__proto__的屬性上,很是相似於new Object進行建立3d

進行拷貝

var a = {
      age: 22,
      data: {
        name: '3333',
      }
    }
    var c = Object.create(a)
    c.age = 666
    c.data.name = 444
    a.age = '18'
    console.log('a', a)
    console.log('c', c)
複製代碼
圖5 Object.create進行拷貝引用變化

咱們可以發現變量c中的__proto__所使用的對象的引用和a對象的引用是一個code

  • 修改c變量的data.name屬性,變量a發生了變化
  • 修改變量a中的age屬性,變量c的__proto__的age屬性也跟着變化

3.展開運算符

在es6中新增的方法;orm

var o = { age: 333, data: { name: 3232 } }
    var d = { ...o }
    d.data.name = 'd修更名字'
    console.log('o',o)
    console.log('d',d)
複製代碼
圖6 展開運算符對象的引用

4.數組拷貝 Array.prototype.concat()

是對數組中的對象進行的一個賦值;

var arr =[12,2323,{name:2}]
var arr2=arr.concat();
arr2[2].name =44
console.log(arr)
console.log(arr2)
複製代碼
圖7 Array.prototype.concat中對象的賦值

Array.prototype.concat 中對於數組的拷貝,其實也是淺拷貝,數組中某個項的對象是存在引用關係的

5.Array.prototype.slice()

實現的原理和上面一致,都是複製基本屬性,裏面的對象引用仍是在一塊兒的 只是複製了一個數組,不會需求原數組中的數據,能夠當成特殊的對象;

深拷貝

深拷貝會另外創造一個如出一轍的對象,新對象跟原對象不共享內存,修改新對象不會改到原對象; 當b進行引用賦值a的時候,會從新生成一個新的地址引用,不會和a共用一個地址;這樣a/b互相改變值的時候不會互相影響;

1.JSON.parse(JSON.string(obj))

JSON.parse(JSON.string(obj)) 是我常常在工做用到的方法,固然也是區分情景的;

var obj = {
      offer: {
        money: 13133,
        company: "一代大神"
      },
      name: 'mfy'
    }
    var obj2 = JSON.parse(JSON.stringify(obj));
    obj2.offer.money = '修改爲自定義的money'
    console.log('obj',obj)
    console.log('obj2',obj2)
複製代碼
圖8 JSON.parse(JSON.string(obj)) 深拷貝對象

😢 缺點

雖然JSON.parse(JSON.string(obj))給咱們提供了很大的便捷度,可是也是存在缺點的,

沒法序列化函數❗️

obj2中是不存在obj1中所包含的函數的

var obj = { 
      fun:function(){console.log("我是函數")},
      name: 'mfy'
    }
    var obj2 = JSON.parse(JSON.stringify(obj)); 
    console.log('obj',obj)
    console.log('obj2',obj2)
複製代碼
圖9 JSON.parse(JSON.string(obj)) 沒法序列化函數
😂 會忽略爲undefined + 會忽略symbol
var a =  Symbol('hh',33);
    var obj = {  
      name: 'mfy',
      age:undefined,
      a,
    }
    var obj2 = JSON.parse(JSON.stringify(obj)); 
    console.log('obj',obj)
    console.log('obj2',obj2)
複製代碼
圖9 JSON.parse(JSON.string(obj)) 忽略未賦值屬性和Symbol類型數據

2.遞歸實現深拷貝

// 未作細化
    function deepMerge(obj) {
      //類型校驗省略
      var target = {};
      for (var key in obj) {
        let itemObj = obj[key];
        if (typeof itemObj == 'object') {
          target[key] = deepMerge(itemObj)
        } else {
          target[key] = obj[key]
        }
      }
      return target;
    }
    
    var obj3 = {age:18,name:'mfy',offer:{company:'XXX',jobs:333}}
    var obj4 = deepMerge(obj3);
    obj4.offer.company="big company"
    console.log('obj4',obj4)
    console.log('obj3',obj3)
複製代碼
圖10 遞歸實現深拷貝

obj4通過深拷貝後,再次修改內部對象類型,不會影響到obj3,此時兩個變量的存儲方式,存儲空間是無任何關聯的

3.使用函數庫lodash

lodash庫中也是提供了方法,去深拷貝,其原理基本和2一致

var _ = require('lodash');
var obj1 = {
    a: 1,
    b: { f: { g: 1 } },
    c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);
複製代碼

總結

操做 第一層基本數據類型改變 第二層爲引用類型改變
賦值 原數據會改變 原數據會改變
淺拷貝 原數據不會改變 原數據會改變
深拷貝 原數據不會改變 原數據不會改變

參考文檔

文章是很久以前進行總結的,相關參考資料因爲當時沒標註,因此沒法找到連接了,但願理解;😂

相關文章
相關標籤/搜索