JavaScript的深浅拷贝解析及手写深拷贝实现

1. 说到JavaScript的深浅拷贝,那么必然得从其‘源头’说起----JavaScript的两大数据类型(基本数据类型和引用数据类型)。

  1. 基本数据类型的特点:数据直接存储在栈中。
  2. 引用数据类型的特点:存储在栈中的是对象的引用,而真实的数据存放在堆内存中。
  • 基本数据类型:String、Number、Boolean、Null、Undefined、Symbol、BigInt。
  • 引用数据类型:Object。

2. 深浅拷贝的区别。

  • 浅拷贝:只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。
  • 深拷贝:会创造一个一模一样的对象,新对象和原对象不共享内存,修改新对象不会改到原对象。

(注:狭义上深浅拷贝只是针对引用数据类型而言的,基本数据类型只是赋值操作)

3. JavaScript中原有的方法实现局限性的“深浅拷贝”。

  • Object.assign({},obj):当obj是单层对象时,此时的Object.assign()能实现“深拷贝”;而当obj为多层对象时,那么Object.assign({},obj)仅能实现“浅拷贝”。
  • JSON.parse(JSON.stringify(xxx)):这种组合能实现普通对象(function,Symbol(),undefined这三种除外)的深拷贝;而实现function,Symbol(),undefined这三种时,会造成数据丢失,故也称不上是完美的深拷贝。

4.那么,在JS中要想实现功能齐全,兼容性强的深拷贝,那么就得自己手写实现深拷贝函数(PS:这也是面试必考题,不是手写也会让你描述实现的主要思想)。

主要实现思想:通过递归思想,遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝。

// 深拷贝:对对象内部进行深拷贝,支持 Array、Date、RegExp、DOM
    function deepCopy(params) {
      // 如果不是对象则退出(可停止递归)
      if (typeof params !== 'object') return;
      // 深拷贝初始值:对象/数组
      let newObj = (params instanceof Array) ? [] : {};
      // 使用 for-in 循环对象属性(包括原型链上的属性)
      for (let i in params) {
        // 只访问对象自身属性
        if (params.hasOwnProperty(i)) {
          // 当前属性还未存在于新对象中时
          if (!(i in newObj)) {
            if (params[i] instanceof Date) {
              // 判断日期类型
              newObj[i] = new Date(params[i].getTime());
            } else if (params[i] instanceof RegExp) {
              // 判断正则类型
              newObj[i] = new RegExp(params[i]);
            } else if ((typeof params[i] === 'object') && params[i].nodeType === 1) {
              // 判断 DOM 元素节点
              let domEle = document.getElementsByTagName(params[i].nodeName)[0];
              newObj[i] = domEle.cloneNode(true);
            } else {
              // 当元素属于对象(排除 Date、RegExp、DOM)类型时递归拷贝
              newObj[i] = (typeof params[i] === 'object') ? deepCopy(params[i]) : params[i];
            }
          }
        }
      }
      return newObj;
    }

猜你喜欢

转载自blog.csdn.net/Yi2008yi/article/details/122479001