面试官:能否用JavaScript实现深度克隆

前言

 前拷贝和深拷贝都是对于JS中的引用类型而言的,浅拷贝就只是复制对象的引用,如果拷贝后的对象发生变化,原对象也会发生变化。只有深拷贝才是真正地对对象的拷贝。

首先,需要知道,JavaScript中的数据类型分为基本数据类型和引用数据类型,对于基本数据类型而言,没有深浅拷贝的区别,我们所说的深拷贝都是对于引用数据类型而言的。

浅拷贝

    const obj = { a: 'a', b: 'b', c: [1, 2, 3], d: { dd: 'dd' } }
    const array = [1, 2, 3, 4, 5]
    const CloneObj = obj
    const CloneArray = array
    CloneObj.a = 'c'
    CloneArray.push(6)
    console.log(CloneArray, CloneObj) 
 //  [1, 2, 3, 4, 5, 6]
   console.log(CloneObj)
 // a: "c"
 // b: "b"
 // c: (3) [1, 2, 3]
 // d: {dd: "dd"}

可以清楚地看到当CloneObj和CloneArray发生变化时,obj和array也发生了变化。

深拷贝

深拷贝就是对目标完全的拷贝,只要进行了深拷贝,他们就不会再互相影响。

目前实现深度拷贝的方法主要有两种:

1. 利用JSON对象中的parse和stringify

2.利用递归来实现每一层重新创建对象并赋值

首先看第一种方式

    // 使用JSON来实现深度克隆
    const obj1 = { a: 'a', b: 'b', c: [1, 2, 3], d: { dd: 'dd' } }
    const CloneObj1 = JSON.parse(JSON.stringify(obj1))
    console.log(obj1 === CloneObj1) // false
    CloneObj1.a = { aa: 'aa' }
    console.log(obj1) //a: "a", b: "b",c: [1, 2, 3],d: {dd: "dd"}
    const array1 = [1, 2, 3, 4, 5]
    const CloneArray1 = JSON.parse(JSON.stringify(array1))
    console.log(array1 === CloneArray1) // false
    CloneArray1.push(6)
    console.log(array1)  // [1, 2, 3, 4, 5]

以上代码确实实现了深度拷贝,但是这种方法只能使用一些简单的情况,比如如果对象中的一个属性值是一个函数,那么使用这个方法会导致属性丢失。

因为undefined、function、symbol会在转换过程中被忽略,所以当一个对象中有函数时,就不能用这个方法进行深拷贝。

第二种方式:递归

  function DeepClone(source) {
      // 判断目标是数组还是对象
      const targetObj = source.constructor === Array ? [] : {}
      for (let key in source) {
        if (source.hasOwnProperty(key)) {
          //   如果是对象就递归
          if (source[key] && typeof source[key] === 'object') {
            targetObj[key] = source[key].constructor === Array ? [] : {}
            targetObj[key] = DeepClone(source[key])
          } else {
            targetObj[key] = source[key]
          }
        }
      }
      return targetObj
    }

首先判断目标的类型,但是要怎么判断呢??? 我们可以根据constructor属性来判断,因为实例对象的constructor属性指向它的构造函数之前我们提到过,只有引用类型的数据才有浅拷贝深拷贝之分,所以接下来就是要判断是不是引用类型,如果是就继续向下进行递归,如果不是则直接赋值,这样就实现了深度拷贝。

那么我们来测试一下:

扫描二维码关注公众号,回复: 11537193 查看本文章
    var a = {
      msg: '213123',
      data: '2018141142',
      getName: function() {
        return 'Bob'
      }
    }
    var b = DeepClone(a)
    b.data = '0'
    console.log(a)  // {msg: "213123", data: "2018141142", getName: ƒ}

可以,没有问题!

补充:JavaScript中拷贝的方法

我们知道在JavaScript中,数组有两个方法 concat 和 slice 是可以实现对原数组的拷贝的,这两个方法都不会修改原数组,而是返回一个修改后的新数组。

同时,ES6 中 引入了 Object.assgn 方法和 ... 展开运算符也能实现对对象的拷贝。

经过我自己的测试,发现他们都是首层浅拷贝。

总结:

1. 赋值运算符=实现的是浅拷贝,只拷贝对象的引用值

2. JavaScript中数组和对象自带的拷贝方法都是首层浅拷贝

3. JSON实现的是深拷贝,但是对目标对象有要求

4. 如果想实现真正意义上的深拷贝,采用上面的递归方式

猜你喜欢

转载自blog.csdn.net/qq_44313091/article/details/106903714
今日推荐