【经典面试题】js深拷贝浅拷贝区别,怎么实现深拷贝

目录

一、区别

浅拷贝(shallow copy):

深拷贝(deep copy)

二、实现方法:

浅拷贝

1、直接赋值法

深拷贝

1、Object.assign()

2、转成JSON

3、递归

4. 通过jQuery的extend方法实现深拷贝

5. lodash函数库实现深拷贝


一、区别

浅拷贝(shallow copy):

        复制指向某个对象的指针,而不复制对象本身,新旧对象共享一块内存;浅拷贝就是只拷贝一层,更深层次对象级别只拷贝引用(地址)当拷贝的新对象发生改变时,原对象也会发生相同的改变,也就是说,浅拷贝会影响原来的元素

深拷贝(deep copy):

     每一级的数据都会拷贝 深拷贝只拷贝内容,两个对象拥有不同的地址,当拷贝出来的对象发生改变时,原对象内容不会改变,两者互不影响

二、实现方法:

浅拷贝

1、直接赋值法:

        var arr = [1,2,3]
        var newarr = arr;
        newarr[1] = 5;
        console.log(arr,newarr);//[1, 5, 3],[1, 5, 3]

深拷贝

1、Object.assign()

当对象中只有一级属性,没有二级属性的时候,此方法为深拷贝,但是对象中有对象的时候,此方法,在二级属性以后就是浅拷贝。

let foo = {
    a: 1,
    b: 2,
    c: {
        d: 1,
    }
}
let bar = {};
Object.assign(bar, foo);
foo.a++;
foo.a === 2 //true
bar.a === 1 //true
foo.c.d++;
foo.c.d === 2 //true
bar.c.d === 1 //false
bar.c.d === 2 //true

2、转成JSON

  用JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象。

 但这种方法的缺陷是会破坏原型链,并且无法拷贝属性值为function的属性

var obj1 = { body: { a: 10 } };
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.body.a = 20;
console.log(obj1);   // { body: { a: 10 } } 
console.log(obj2);   // { body: { a: 20 } }
console.log(obj1 === obj2);   // false
console.log(obj1.body === obj2.body);   // false

3、递归

  采用递归的方法去复制拷贝对象

//使用递归的方式实现数组、对象的深拷贝
function deepClone1(obj) {
  //判断拷贝的要进行深拷贝的是数组还是对象,是数组的话进行数组拷贝,对象的话进行对象拷贝
  var objClone = Array.isArray(obj) ? [] : {};
  //进行深拷贝的不能为空,并且是对象或者是
  if (obj && typeof obj === "object") {
    for (key in obj) {
      if (obj.hasOwnProperty(key)) {
        if (obj[key] && typeof obj[key] === "object") {
          objClone[key] = deepClone1(obj[key]);
        } else {
          objClone[key] = obj[key];
        }
      }
    }
  }
  return objClone;
}

4. 通过jQuery的extend方法实现深拷贝

var array = [1,2,3,4];
var newArray = $.extend(true,[],array);

jQuery.extend源码

// extend方法为jQuery对象和init对象的prototype扩展方法
        // 同时具有独立的扩展普通对象的功能
        jQuery.extend = jQuery.fn.extend = function() {
          /*
          *target被扩展的对象
          *length参数的数量
          *deep是否深度操作
          */
          var options, name, src, copy, copyIsArray, clone,
            target = arguments[0] || {},
            i = 1,
            length = arguments.length,
            deep = false;

          // target为第一个参数,如果第一个参数是Boolean类型的值,则把target赋值给deep
          // deep表示是否进行深层面的复制,当为true时,进行深度复制,否则只进行第一层扩展
          // 然后把第二个参数赋值给target
          if ( typeof target === "boolean" ) {
            deep = target;
            target = arguments[1] || {};

            // 将i赋值为2,跳过前两个参数
            i = 2;
          }

          // target既不是对象也不是函数则把target设置为空对象。
          if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
            target = {};
          }

          // 如果只有一个参数,则把jQuery对象赋值给target,即扩展到jQuery对象上
          if ( length === i ) {
            target = this;

            // i减1,指向被扩展对象
            --i;
          }

          // 开始遍历需要被扩展到target上的参数

          for ( ; i < length; i++ ) {
            // 处理第i个被扩展的对象,即除去deep和target之外的对象
                 //options  是来接收除了第一个参数外的所有参数。中间桥梁的作用吧
            if ( (options = arguments[ i ]) != null ) {
              // 遍历第i个对象的所有可遍历的属性
              for ( name in options ) {
                // 根据被扩展对象的键获得目标对象相应值,并赋值给src
                src = target[ name ];
                // 得到被扩展对象的值
                copy = options[ name ];
                            // Prevent never-ending loop
                            // 若参数中字段的值就是目标参数,停止赋值,进行下一个字段的赋值
                            // 这是为了防止无限的循环嵌套
                            //var _default = {name : 'wenzi'};
                            //var obj = {name : _default}
                            //$.extend(_default, obj);
                            //如果没有这个判断,则结果是:_default = {name : _default};
                            //如果有这个判断,则结果是:_default = {name : wenzi};
                           // _default是object类型,里面有个字段name,值是_default,而_default是object类型,里面有个字段name,值是_default......,无限的循环下去。于是jQuery中直接不进行操作,跳过这个字段,进行下一个字段的操作。

                if ( target === copy ) {
                  continue;
                }

                // 当用户想要深度操作时,递归合并
                // copy是纯对象或者是数组
                if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
                  // 如果是数组
                  if ( copyIsArray ) {
                    // 将copyIsArray重新设置为false,为下次遍历做准备。
                    copyIsArray = false;
                    // 判断被扩展的对象中src是不是数组
                    clone = src && jQuery.isArray(src) ? src : [];
                  } else { 
                    // 判断被扩展的对象中src是不是纯对象
                    clone = src && jQuery.isPlainObject(src) ? src : {};
                  }

                  // 递归调用extend方法,继续进行深度遍历
                  target[ name ] = jQuery.extend( deep, clone, copy );

                // 如果不需要深度复制,则直接把copy(第i个被扩展对象中被遍历的那个键的值)
                } else if ( copy !== undefined ) {
                  target[ name ] = copy;
                }
              }
            }
          }

          // 原对象被改变,因此如果不想改变原对象,target可传入{}
          return target;
        };

5. lodash函数库实现深拷贝

lodash很热门的函数库,提供了 lodash.cloneDeep()实现深拷贝

猜你喜欢

转载自blog.csdn.net/m0_64346035/article/details/124859150