深入浅拷贝和深拷贝原理

浅拷贝: 创建一个新的对象,赋值给新的函数; 如果这个对象是基本数据类型,就是复制一份给了新的函数, 如果是引用类型,就是复制一份内存中的地址,改变一个值肯定会影响另一个对象
深拷贝: 把原来的数据源复制了一份,放到新的内存开辟空间里,两个数据源互不影响

先看看:浅拷贝

方法一: object.assign

objetct.assign 是 ES6 中object 的一个方法,可以用来浅拷贝

    // 方法一 object.assign
    // Object.assign(target, ...source) target:目标, source:目标源
    let target = {}
    let source = { a : { b : 1}}
    let source1 = { c : { d : 1}}
    Object.assign(target, source, source1)
    console.log(target)  // { a: { b: 1}, c: { d: 2} }
    target.c.d= 2
    console.log(target); // { a: { b: 1}, c: { d: 2} }

注意几点:
1.不能拷贝对象的继承属性
2.不能拷贝对象的不可枚举的属性
3.可以拷贝Symbol 类型的属性
可以这样理解:Object.assign 循环遍历原对象的属性,通过复制的方法 将赋值给目标对象的相应属性

    let obj1 = {a:{b:1},sym:Symbol(1)}
        Object.defineProperty(obj1,"innumerable",{
          value:"不可枚举属性",
          enumerable:false
        })
    let obj2 = {}
    Object.assign(obj2,obj1)
    obj1.a.b = 2
    console.log("obj1",obj1);
    console.log("obj2",obj2);

打印结果

方法二: 扩展运算符方式:

和object.assign 有着同样的缺陷

    // 对象拷贝
    let obj = { a:1, b:{ c: 1}}
    let obj2 = {...obj}
    obj.a = 2
    console.log(obj) // { a:2, b:{c:1}}
    obj.b.c = 2
    console.log(obj)// { a:2, b:{c:2}}

    // 数组拷贝
    let arr = [1,2,3,4]
    let arr2 = [...arr]
    console.log(arr1); // [1,2,3,4]
    console.log(arr2); // [1,2,3,4]

方法三:  concat 拷贝数组

数组的concat 方法也是一个浅拷贝, concat 使用比较有局限性,只能使用数组中
 

    // concat 拷贝数组
    let arr = [1,2,3,4]
    let arr1 = arr.concat()
    arr1[0] = 10
    console.log(arr)    // [1, 2, 3, 4]
    console.log(arr1)   // [10, 2, 3, 4]

方法四: slice 拷贝数组

slice 方法也比较有局限性,只能使用数组类型

    // slice 方法
    let arr = [1,2,3,4]
    let arr1 = arr.slice()
    arr1[0] = 20
    console.log(arr)    // [1, 2, 3, 4]
    console.log(arr1)   // [20, 2, 3, 4]

上面的代码中可以看出,浅拷贝只能拷贝一层的对象,如果存在对象的嵌套,那么浅拷贝将无能为力;

因此深拷贝是为解决浅拷贝的问题
 

上面写了这么多,我的想法,能不能自己写一个浅拷贝
注意两点:

1.基本数据类型,基本的拷贝
2.引用类型,拷贝一层对象属性

   const shallowCopy = (obj) => {
      if(typeof obj === "object" && obj != null){
        console.dir(obj);
        let copy = Array.isArray(obj) ? [] : {}
        for(let prop in obj){
          console.log(prop,"prop");
          if(obj.hasOwnProperty(prop)){
            copy[prop] = obj[prop]
          }
        }
        console.log(copy,"copy");
        return copy
      }else{
        return obj
      }
    }
    let b = ["a","b","c"]
    let a ={
      age:12
    }
   let c= shallowCopy(b)
   console.log(c);


 

深拷贝:

1.先看看我们常用的 JSON.stringify(),把一个对象序列化成为 JSON 的字符串,并将对象里面的内容转换成字符串,最后再用 JSON.parse() 的方法将JSON 字符串生成一个新的对象

    let obj1 = { a:1, b:[1,2,3]}
    let str = JSON.stringify(obj1)
    let obj2 = JSON.parse(str)
    obj1.a = 2
    obj2.b[0]= 10
    console.log(obj1)   // {a:2 , b:[1,2,3]}
    console.log(obj2)   // {a:1 , b:[10,2,3]}

通过 JSON.stringify 实现一个对象的深拷贝,通过改变 obj1 的 a属性,改变 obj2 的 b中数组,看结果,互相不影响, 这也是我们常用的

2. 通过递归的方式,写一个简单的递归

    let obj1 = { a:1, b:[1,2,3],c:{d:1} }

    function deepCopy(obj){
      let copyObj = {}
      for(let key in obj){
        if(typeof obj[key] == "object"){
          copyObj[key] = deepCopy(obj[key])
        }else{
          copyObj[key]= obj[key]
        }
      }
      return copyObj
    }
    let obj2 = deepCopy(obj1)
    obj2.a = 10
    obj2.b[0] = 100
    obj2.c.d = 2
    console.log(obj2);

3. 还可以通过 $.extend() , 我对它的理解是 $.extend({},src1,src2), 将src1和src2 合并一个对象,如果有相同的,后面会覆盖前面的,

4.改善递归
 

const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null)
const deepClone = function (obj, hash = new WeakMap()) {
  if (obj.constructor === Date) 
  return new Date(obj)       // 日期对象直接返回一个新的日期对象
  if (obj.constructor === RegExp)
  return new RegExp(obj)     //正则对象直接返回一个新的正则对象
  //如果循环引用了就用 weakMap 来解决
  if (hash.has(obj)) return hash.get(obj)
  let allDesc = Object.getOwnPropertyDescriptors(obj)
  //遍历传入参数所有键的特性
  let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc)
  //继承原型链
  hash.set(obj, cloneObj)
  for (let key of Reflect.ownKeys(obj)) { 
    cloneObj[key] = (isComplexDataType(obj[key]) && typeof obj[key] !== 'function') ? deepClone(obj[key], hash) : obj[key]
  }
  return cloneObj
}
// 下面是验证代码
let obj = {
  num: 0,
  str: '',
  boolean: true,
  unf: undefined,
  nul: null,
  obj: { name: '我是一个对象', id: 1 },
  arr: [0, 1, 2],
  func: function () { console.log('我是一个函数') },
  date: new Date(0),
  reg: new RegExp('/我是一个正则/ig'),
  [Symbol('1')]: 1,
};
Object.defineProperty(obj, 'innumerable', {
  enumerable: false, value: '不可枚举属性' }
);
obj = Object.create(obj, Object.getOwnPropertyDescriptors(obj))
obj.loop = obj    // 设置loop成循环引用的属性
let cloneObj = deepClone(obj)
cloneObj.arr.push(4)
console.log('obj', obj)
console.log('cloneObj', cloneObj)

 


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值