代码

可以实现数组或对象深拷贝,但不能处理函数,undefined

        function depClone(obj) {
            var result = JSON.parse(JSON.stringify(obj));
            return result;
        }
        var obj = {
            family: {
                border: "wangzhipeng",
                father: "wanglicai",
                mother: "sunaiyun"
            },
            name: "gino",
            sex: "male",
            age: "27"
        };

        var obj1 = depClone(obj)

        obj1.family.border = 'aa'
        console.log(obj) // 原对象没有改变
        console.log(obj1) //新对象有改变

效果

clipboard.png

深浅拷贝

判断对象是否有此属性

in与hasOwnProperty

所有继承了 Object 的对象都会继承到 hasOwnProperty 方法。这个方法可以用来检测一个对象是否含有特定的自身属性;和 in 运算符不同,该方法会忽略掉那些从原型链上继承到的属性

      function Test (name) {
        this.name = name
        this.log = function () {
          console.log('this.name', this.name)
        }
      }

      Test.prototype.sex = 22
      const t = new Test()
      t.age = 11
      for (let key in t) {
           console.log('key', key) //name,log,sex,age
        if (t.hasOwnProperty(key)) {
          console.log('key', key) //name,log,age
        }
      }

JSON

  • 缺点
  • 会忽略掉对象中属性值为undefined和函数的属性 由于方法的底层
  • 实现用了递归,如果对象存在循环引用,会爆栈(报循环引用的错)

递归

  • 优点
  • 可以解决JSON方式忽略属性值为undefined和function的属性的问题
  • 对象存在循环引用时仍然会爆栈
        function deepCopy (obj) {
          const target = {}
          for (let key in obj) {
            if (obj.hasOwnProperty(key)) {
              // key为对象自身可枚举的属性
              if (
                Object.prototype.toString.call(obj[key]) === '[object Object]'
              ) {
                // 属性值为对象,递归调用
                target[key] = deepCopy(obj[key])
              } else {
                target[key] = obj[key]
              }
            }
          }
          return target
        }

闭包 + 递归

解决了JSON方式和递归方式存在的问题,可实现真正的深拷贝
      function deepCopy (copyObj) {
        // 用来记录已经拷贝过的属性值为对象的属性以及属性的值,解决递归循环引用对象的爆栈问题
        const cache = {}

        // 拷贝对象
        function copy (obj) {
          const target = {}
          for (let key in obj) {
            if (obj.hasOwnProperty(key)) {
              // key为对象自身可枚举的属性
              if (
                Object.prototype.toString.call(obj[key]) === '[object Object]'
              ) {
                // 属性值为对象
                if (cache[obj[key]]) {
                  // 说明该属性已经被拷贝过一次,现在又拷贝,证明出现了循环引用
                  target[key] = cache[obj[key]]
                } else {
                  cache[obj[key]] = obj[key]
                  target[key] = copy(obj[key])
                }
              } else {
                target[key] = obj[key]
              }
            }
          }
          return target
        }
        return copy(copyObj)
      }

测试案例

const obj1 = {a: 'a', b: {c: 'cc'},d:function(){}}
console.log('源对象: ', obj1) // 源对象:  {a: "a", b: {c: "cc"}}
const copyObj1 = deepCopy(obj1)
console.log('拷贝后的目标对象: ', copyObj1) // 拷贝后的目标对象:  {a: "a", b: {c: "cc"}}
console.log('-----改变目标对象的属性---------')
copyObj1.b = 'bb'
console.log('源对象: ', obj1) // 源对象:  {a: "a", b: {c: "cc"}}
console.log('目标对象: ', copyObj1) // 目标对象:  {a: "a", b: "bb"}

// 示例二, 对象存在循环引用
const objLoop = {}
const b = {objLoop}
objLoop.b = b
console.log('源对象: ', objLoop) // 源对象: {b: {objLoop: {b: {objLoop: {b: ...}}}}}
const copyObjLoop = deepCopy(objLoop)
console.log('目标对象: ', copyObjLoop) // 目标对象: {b: {objLoop: {b: {objLoop: {b: ...}}}}}
console.log('-----改变目标对象------')
copyObjLoop.b = 'bb'
console.log('源对象: ', objLoop) // 源对象: {b: {objLoop: {b: {objLoop: {b: ...}}}}}
console.log('目标对象: ', copyObjLoop) // 目标对象: {b: "bb"}

参考


渣渣辉
1.3k 声望147 粉丝