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);
复制代码

总结

操做 第一层基本数据类型改变 第二层为引用类型改变
赋值 原数据会改变 原数据会改变
浅拷贝 原数据不会改变 原数据会改变
深拷贝 原数据不会改变 原数据不会改变

参考文档

文章是很久以前进行总结的,相关参考资料因为当时没标注,因此没法找到连接了,但愿理解;😂

相关文章
相关标签/搜索