JavaScript训练合集——深拷贝

141 阅读2分钟

在JavaScript中进行数据拷贝的时候, 根据数据的类型分为深拷贝和浅拷贝, 如果拷贝的数据为基本类型, 浅拷贝即可, 如果拷贝的类型是引用类型, 想真正意义的进行拷贝, 就需要进行深拷贝

这里就不详细讲述深拷贝和浅拷贝的区别了, 它们的区别是因为基本类型和引用类型的数据类型造成的.

基本类型可以通过a = b, 这种方式进行拷贝, 但是如果是引用类型的话, a = b, 只是把b的内存地址拷贝给了a, 这样就会造成, 拷贝完之后我改变b变量的某些值时, a也会被改变, 因为它们的内存地址是一样的, 这时候就需要进行深拷贝。

深拷贝的方法也有很多很多, 像比较简单的通过JSON.stringify + JSON.parse就可以实现深拷贝, 但是如果我们在面试的时候被问到, 面试官是打算让你用下面这种方式实现, 可以考察你的基本功。

var obj = {
  name: "CreatorRay",
  age: 21,
  info: {
    hobby: [
      "play",
      "game",
      {
        a: 1,
      },
    ],
    career: {
      teacher: 4,
      engineer: 9,
    },
    play: () => {  console.log(1111); }
  },
};

这个obj就是我们要进行深拷贝的数据, 这个数据中各种类型都用到了, 可以验证是否实现了深拷贝

function deepClone (origin, target) {
    let tar = target || {};
    let toStr = Object.prototype.toString;
    let arrType = '[object Array]';
    
    for (let k in origin) {
        // 是自己的属性再进行拷贝
        if (origin.hasOwnProperty(k)) {
            if (typeof origin[k] === 'object' && origin[k] !== null) {
                tar[k] = toStr.call(origin[k]) === arrType ? [] : {};
                deepClone(origin[k], tar[k]);
            }else {
                tar[k] = origin[k];
            }
        }
    }
    
    return tar;
}

先说一下思路:

  • 进行拷贝之前要先保证, 要拷贝的值是存在的, 如果不存在就赋一个空对象
  • 对被拷贝的数据进行for in循环, 进行拷贝之前要先保证现在拷贝的属性是自己的
  • 对属性进行拷贝的时候要区分现在的属性是什么类型, 基本类型的话就直接拷贝, 引用类型的话就继续对当前属性进行递归
  • 在判断当前属性是否是对象或者数组的时候, 方法也很多, 我这里用到的是Object.property.toString方法, 这个方法要返回一个[object xxx] 的字符串, 如果是数组的话就是[object Array], 如果是对象的话就是[object Object], 在用的时候继续改变一下this指向。

主体思路就是这些, 当前要实现一个完美的深拷贝还要考虑很多很多的细节。

下面我们来验证一下写的是否正确

const newObj = deepClone(obj, {});
newObj.info.hobby[2].a = 999;
console.log(obj, newObj);

image.png

可以看到我们通过deepClone拷贝完之后, 改变其中一个对象的属性中某个值后, 另一个对象相应的值并没有受到影响, 说明我们这个深拷贝函数写的没有问题。