js对象浅拷贝与深拷贝

一、简介

   浅拷贝是拷贝一层,如果数据是基本数据类型,会拷贝其本身,如果除了基本数据类型之外还有一层对象,那么只能拷贝其引用,对象的改变会反应到拷贝对象上。
   深拷贝是拷贝多层,每一层的数据都会拷贝出来,对象的改变不会影响拷贝对象。

二、实现

   1、实现浅拷贝
     
1)数组自带的浅拷贝方法:slice()、concat、Array.from()、... 操作符

let arr = [1, 2, 3];
let arr1 = arr;
//以下4种方式实现数组浅拷贝
let arr2 = arr.slice();
let arr2 = [...arr]
let arr2 = [].concat(arr);
let arr2 = Array.from(arr)
arr[0] = 4;
console.log(arr, arr1, arr2)


通过上图我们可以看到,arr1不通过浅拷贝直接赋值, 此时arr1复制的是arr的引用,arr值改变arr1也会改变,而浅拷贝则不会。


      2)对象自带的浅拷贝方法:... 操作符、Object.assign()

  let obj = {
    a: 1,
    b: 2
  }
  let obj1 = obj;
  //以下是对象两种自带的浅拷贝方法
  let obj2 = Object.assign({}, obj)
  let { ...obj2 } = obj;
  obj.a = 3;
  console.log(obj, obj1, obj2)

通过上图我们可以看到,obj1不通过浅拷贝直接赋值, 此时obj1复制的是obj的引用,obj值改变obj1也会改变,而浅拷贝则不会。

2.实现深拷贝

 1)通过JSON.parse(JSON.stringify(obj))
    这个方法简单,平常项目也常拿来使用。缺点:会忽略undefined、任意的函数、正则、symbol 值等; Date类型会转为字符串

 2)简单实现深拷贝方法

function deepClone(obj){
    let objClone = Array.isArray(obj)?[]:{};
    if(obj && typeof obj==="object"){
        for(key in obj){
            if(obj.hasOwnProperty(key)){
                //判断ojb子元素是否为对象,如果是,递归复制
                if(obj[key]&&typeof obj[key] ==="object"){
                    objClone[key] = deepClone(obj[key]);
                }else{
                    //如果不是,简单复制
                    objClone[key] = obj[key];
                }
            }
        }
    }
    return objClone;
}

目前只考虑简单的对象和数组两种情况,没有考虑日期,正则等等其他情况。主要是,当对象的子项还是对象类型时,递归调用。

考虑更多情况深拷贝实现

   function isType(obj, type) {
      if (typeof obj !== 'object') return false;
      return Object.prototype.toString.call(obj).replace('[object ', '').replace(']', '') === type
    }
    const getRegExp = re => {
      var flags = '';
      if (re.global) flags += 'g';
      if (re.ignoreCase) flags += 'i';
      if (re.multiline) flags += 'm';
      return flags;
    };
    const clone = parent => {
      // 维护两个储存循环引用的数组
      const parents = [];
      const children = [];

      const _clone = parent => {
        if (parent === null) return null;
        if (typeof parent !== 'object') return parent;

        let child, proto;

        if (isType(parent, 'Array')) {
          // 对数组做特殊处理
          child = [];
        } else if (isType(parent, 'RegExp')) {
          // 对正则对象做特殊处理
          child = new RegExp(parent.source, getRegExp(parent));
          if (parent.lastIndex) child.lastIndex = parent.lastIndex;
        } else if (isType(parent, 'Date')) {
          // 对Date对象做特殊处理
          child = new Date(parent.getTime());
        } else {
          // 处理对象原型
          proto = Object.getPrototypeOf(parent);
          // 利用Object.create切断原型链
          child = Object.create(proto);
        }

        // 处理循环引用
        const index = parents.indexOf(parent);

        if (index != -1) {
          // 如果父数组存在本对象,说明之前已经被引用过,直接返回此对象
          return children[index];
        }
        parents.push(parent);
        children.push(child);

        for (let i in parent) {
          // 递归
          child[i] = _clone(parent[i]);
        }

        return child;
      };
      return _clone(parent);
    };
    function person(pname) {
      this.name = pname;
    }

    const Messi = new person('Messi');

    function say() {
      console.log('hi');
    }

    const oldObj = {
      a: say,
      c: new RegExp('ab+c', 'i'),
      d: Messi,
    };

    oldObj.b = oldObj;
    const newObj = clone(oldObj);
    console.log(newObj)

3.有些第三方库也封装好了深拷贝的方法
如 loadash的cloneDeep方法

你可能感兴趣的:(JavaScript,面试)