前端项目中关于深拷贝和浅拷贝的遇见

项目初次接触:获取的数据赋值给变量,经过开关按钮切换后,数据变了,发现原来只是浅拷贝了。

深拷贝:也叫 值拷贝

浅拷贝: 也叫 引用拷贝

浅拷贝就只是复制对象的引用

  原始类型 Undefined,Null,Boolean, Number,String 是存入堆中,直接引用

  Object, Array则是存入栈中,只用一个指针来引用值,如果拷贝后的对象发生变化,原对象也会发生变化

 

  对象数据存放在堆内存中,对象变量存放在栈内存中,对象变量通过引用数据的堆地址实现对象访问

 

js 中的深拷贝(值拷贝)

  基本数据类型:String, Number, Boolean, Undefined, Null ,在赋值的过程中都是值拷贝

js 中的浅拷贝(引用拷贝)

  js 中的对象数据类型: Object , Array, Function, Map, Set ,在赋值过程中都是引用拷贝

将浅拷贝转换成深拷贝

  Array 的深拷贝

    通过 slice 方法

      slice() 方法操作数组时,不会对原数组有影响,会产出一个新的数组

        slice() 方法选择从给定的start参数开始的元素,并在给定的end 参数处结束,但不包括。

    通过 concat 方法

      数组的 concat() 方法,能够连接两个数组,同样不会改变原来的数组。用一个空数组连接另一个数组,即可实现深拷贝

        let arr1 = [1,2,3];

        let arr 2 = [].concat(arr1)

    通过 ES6语法中 解构 ...

      ES6 语法中的结构 ... 经常在数组的深拷贝中用到

        let arr1 = [0,1,2]

        let arr2 = [...arr1]

    通过Array.forn方法

      Array.from() 方法能从一个类似数组或可迭代对象中返回一个新的数组实例。通过Array.from()方法能获取到一个数组的深拷贝

        let arr1 = [1,2,3]

        let arr2 = Array.from(arr1)

  Object的浅拷贝

    通过Object.assign()方法

      Object.assign(target,...source)

      let copyobj = Object.assign({}, sourceobj);

      针对深拷贝问题,需要使用其他办法,因为Object.assign()只复制属性值

    通过展开语法实现浅拷贝

      let copyObj = {...target}

    通过Object.create() 实现浅拷贝

      let cloneObj = Object.create(

        Object.getPrototypeof(target),

        Object.getOwnPropertyDescriptors(target))

    

   Object 深拷贝

    通过JSON转换实现深拷贝

      let copyObj = JSON.parse(JSON.stringify(target))

       也会存在一些问题: 对某些数据不支持:

          如Date类型会被转为字符串类型,

          Undefined和RegExp类型丢失等问题。

          无法拷贝存在循环引用的对象。

          拷贝自身可枚举字符串属性,原型链丢失。

          属性特性丢失。 性能较差。

    手动实现深拷贝

     

function deepClone(target) {
    if (!(target instanceof Object) || 'isClone' in target)
        return target;
    
    let clone = null;
    if (target instanceof Date)
        clone = new target.constructor(); 
    else if(Array.isArray(target))
        clone = [];
    else
        clone = new target.constructor();
    let keys = Reflect.ownKeys(target);
    for (let key of keys) {
        if (Object.prototype.hasOwnProperty.call(target, key)) {
            target['isClone'] = null;
            clone[key] = deepClone(target[key]);
            delete target['isClone'];
        }
    }
    return clone;
}

 

 

  

方法二:项目中

export function deepClone(source) {
  if (!source && typeof source !== 'object') {
    throw new Error('error arguments', 'deepClone')
  }
  const targetObj = source.constructor === Array ? [] : {}
  Object.keys(source).forEach(keys => {
    if (source[keys] && typeof source[keys] === 'object') {
      targetObj[keys] = deepClone(source[keys])
    } else {
      targetObj[keys] = source[keys]
    }
  })
  return targetObj
}

 

 

方法三

function deepClone(obj) {
    let copy = Object.create(Object.getPrototypeOf(obj));
    let propertyNames = Object.getOwnPropertyNames(obj);
    propertyNames.forEach(function (items) {
        let item = Object.getOwnPropertyDescriptor(obj, items);
        Object.defineProperty(copy, items, item);

    });
    return copy;
}
let obj = {
    name:'lily',
    arr:[1,2,3],
    date: [new Date(1536627600000), new Date(1540047600000)],
    RegExp: new RegExp('\\w+'),
    job:undefined,
    obj2:{
        fun:function(){}
    }
};
let testObj = deepClone(obj);
testObj.name = '不知火舞';
console.log(obj);   //{ name: 'lily',
                    // arr: [ 1, 2, 3 ],
                    // date: [ 2018-09-11T01:00:00.000Z, 2018-10-20T15:00:00.000Z ],
                    // RegExp: /\w+/,
                    // job: undefined,
                    // obj2: { fun: [Function: fun] } }
console.log(testObj)   //{ name: '不知火舞',
                        // arr: [ 1, 2, 3 ],
                        // date: [ 2018-09-11T01:00:00.000Z, 2018-10-20T15:00:00.000Z ],
                        // RegExp: /\w+/,
                        // job: undefined,
                        // obj2: { fun: [Function: fun] } }

 

参考:https://juejin.cn/post/6872765382898221064#heading-8

posted @ 2022-08-19 21:04  zcm花开不败  阅读(172)  评论(0编辑  收藏  举报