JS深浅克隆(复制)函数的封装

在学习JS过程中,深克隆和浅克隆是我们一定会遇到的知识点,之前也学习过,但是如何去实现一个深复制和浅复制并没有去实践。今天就来剖析一下他们的概念和实现的具体操作

1.浅复制

简单来说就是复制该对象的指针而不是复制该对象,两个指针指向同一个地址(数据)

// 方法一
let obj1 = {name: "Bob"};
let obj2 = obj1;
console.log(obj2) // {name: "Bob"}

//方法二 使用 ES6 的新方法进行复制
let obj1 = {name: "Bob"};
let obj2 = Object.assign({}, obj1)   //Object.assign()用于合并对象
console.log(obj2) // {name: "Bob"}

上面的代码为浅复制,obj2 直接复制了 obj1 的指针(此时两个对象共同指向同一个内存) ,所以此时若是改动任意对象的属性,那么两个对象的属性都会改变。
注意:使用Object.assign()复制对象时,第一层为深复制(主要是第一层为原始值)

再来看看下面的例子

let obj1 = {
    name: "bob",
    age: 18,
    sex: "man",
  } 
let obj2 = {}

for(let key in obj1){
  obj2[key] = obj1[key] // 可以使用 Object.assign() 方法代替
  // 将 obj1 的所有属性遍历并赋值给 obj2
}
obj1.name = "Tom" 
console.log(obj1)// obj1 = {name: "Tom", age: 18, sex: "man"} 
console.log(obj2)// obj1 = {name: "bob", age: 18, sex: "man"} 

上面的代码可以看到,我们将 obj1 的所有属性遍历并赋值给 obj2
这时,改变其中一个对象的属性,并不会影响到另一个对象的属性。
注意:它仍然是一个浅复制,因为它只对第一层进行了遍历赋值,如果对多层嵌套的属性进行操作(也可以理解为引用值),依然无能为力,如下

  let obj1 = {
    name: "bob",
    age: 18,
    sex: "man",
    son:{
      one: "Funk"
    } // 此属性也是一个对象
  } 

  let obj2 = {} 

  for(let key in obj1){
    obj2[key] = obj1[key]
    // 将 obj1 的所有属性遍历并赋值给 obj2
  }
  
  obj1.son.one = "Tom" 
console.log(obj1)// obj1 = {name: "Tom", age: 18, sex: "man", son:{one: "Tom"}} 
console.log(obj2)// obj1 = {name: "Tom", age: 18, sex: "man", son:{one: "Tom"}} 

上面可以看出,在进行第二层代码的操作时,改变一个对象的属性时,另一个对象的属性也一并改变了。

弄清楚浅复制之后,我们来封装一个浅复制的功能函数

function shallowCopy(origin, target){
  for(let key in origin){
    if (origin.hasOwnProperty(key)) { // 过滤掉原型的属性,只复制对象本身的属性
      target[key] = origin[key]
    }
  }
}

let obj1 = {state: "复制成功"} 
let obj2 = {} 
shallowCopy(obj1, obj2) // 调用浅复制的封装函数
console.log(obj2) // {state: "复制成功"}

封装成功

2.深复制

理解了浅复制的概念,那么深复制就更加容易理解了。
简单来说就是直接复制值,而不是复制值的指针

之前对浅复制我封装了一个函数,如果只看第一层,也可看做是一个深复制操作。
所以说,如果要封装深复制的操作,就等于给每一层都进行这样的操作(递归)
如下:

function deepCopy(origin, target){
  for(let key in origin){
    if (origin.hasOwnProperty(key)) { 
    // 过滤掉原型的属性,只复制对象本身的属性
      if (typeof(origin[key]) === 'object' && origin[key] !== null) { 
      // 判断属性是否为引用值。null也是对象。
        if (Object.prototype.toString.call(origin[key]) === '[object Array]') {  
        //判断属性是否为数组
          target[key] = [];
        }else{
          target[key] = {};
        }
        deepCopy(origin[key], target[key]) 
        // 若属性为引用值,再次调用此函数,处理该属性
      }else{
        target[key] = origin[key] 
        // 值为原始值,直接复制函数属性
      }
    }
  }
}

封装完成,此时可以带入对象进行复制操作

下面来归纳一下深复制操作的核心思想

深复制本质上就是直接复制值,而不是引用地址。
而上面深复制的封装函数的核心思想,就是对Object的每一层都进行赋值。
步骤如下:

  1. for in 循环,遍历所有对象的属性
  2. 对遍历的属性进行判断,使用origin.hasOwnProperty(key),过滤掉原型的属性,只复制对象本身的属性
  3. 再判断属性是否为引用值(也就是对象)。若不是引用值,则直接复制该属性。若是引用值,则执行完第4步后再次调用这个函数(递归)
  4. 再使用Object.prototype.toString.call(origin[key])判断属性是否为数组。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值