js 赋值、浅拷贝、深拷贝、深拷贝的循环引用问题

33 篇文章 0 订阅
let originArray = [1, [2, 3], [4, 5], {a: 6}];
let cloneArray = ...;
cloneArray[0] = 10;
cloneArray[1] = [20, 30]
cloneArray[2].push('new');
cloneArray[3].a = 60;

1 赋值

对于基本类型赋值,系统会为新的变量在栈内存中分配一个新值
对于引用类型赋值,系统会为新的变量在内存中分配指向此对象的引用

let cloneArray = originArray;
console.log(originArray) // [10, [20, 30], [4, 5, 'new'], {a: 60}]
console.log(cloneArray) // [10, [20, 30], [4, 5, 'new'], {a: 60}]

2 浅拷贝

如果拷贝的是对象类型,只会对第一层进行拷贝,不能对多维对象进行深拷贝
如果对象的属性是一个对象,此时拷贝的就是此属性的引用

2.1 originArray.concat()

返回值是对数组的第一层进行深拷贝

let cloneArray = originArray.concat();
console.log(originArray) // [1, [2, 3], [4, 5, 'new'], {a: 60}]
console.log(cloneArray) // [10, [20, 30], [4, 5, 'new'], {a: 60}]
2.2 originArray.slice()

返回值是对数组的第一层进行深拷贝

let cloneArray = originArray.slice();
console.log(originArray) // [1, [2, 3], [4, 5, 'new'], {a: 60}]
console.log(cloneArray) // [10, [20, 30], [4, 5, 'new'], {a: 60}]
2.3 Object.assign(originObject)

返回值是对对象的第一层进行深拷贝

2.4 解构赋值(...·)

返回值是对目标的第一层进行深拷贝

let cloneArray = [...originArray];
console.log(originArray) // [1, [2, 3], [4, 5, 'new'], {a: 60}]
console.log(cloneArray) // [10, [20, 30], [4, 5, 'new'], {a: 60}]
2.5 自定义
function shallowClone(obj) {
     if (obj != null && typeof obj === 'object') {
          let newobj = obj instanceof Array ? [] : {};
          for (let key in obj) {
              if (obj.hasOwnProperty(key)) {
                  newobj[key] = obj[key]
               }
           }
           return newobj
      }
      else {
          return obj;
      }
 }
let cloneArray = shallowClone(originArray);
console.log(originArray) // [1, [2, 3], [4, 5, 'new'], {a: 60}]
console.log(cloneArray) // [10, [20, 30], [4, 5, 'new'], {a: 60}]

3 深拷贝

对目标的完全拷贝,值也被复制
深拷贝之后,两者不会互相影响

3.1 JSON.parse(JSON.stringify(obj))

会忽略undefined
会忽略symbol
会忽略函数,无法实现对象中方法的深拷贝
不能解决循环引用的对象 (会报错)

let cloneArray = JSON.parse(JSON.stringify(originArray));;
console.log(originArray) // [1, [2, 3], [4, 5], {a: 6}]
console.log(cloneArray) // [10, [20, 30], [4, 5, 'new'], {a: 60}]
3.2 递归
function deepClone(obj) {
     if (obj != null && typeof obj === 'object') {
          let newobj = obj instanceof Array ? [] : {};
          for (let key in obj) {
              if (obj.hasOwnProperty(key)) {
                  newobj[key] = deepClone(obj[key])
               }
           }
           return newobj
      }
      else {
          return obj;
      }
 }
let cloneArray = deepClone(originArray);
console.log(originArray) // [1, [2, 3], [4, 5], {a: 6}]
console.log(cloneArray) // [10, [20, 30], [4, 5, 'new'], {a: 60}]

循环引用时不能用上述方法

3.3 lodash中的cloneDeep()
let cloneArray = cloneDeep(originArray);
console.log(originArray) // [1, [2, 3], [4, 5, 'new'], {a: 60}]
console.log(cloneArray) // [10, [20, 30], [4, 5, 'new'], {a: 60}]
3.4 递归解决循环引用 + symbol
function deepClone(obj, hash = new WeakMap()) {
    if (obj != null && typeof obj === 'object') {
        if (hash.has(obj)) {
             return hash.get(obj);
         }
        let newobj = Array.isArray(obj) ? [] : {};
        hash.set(obj, newobj);
        let symKeys = Object.getOwnPropertySymbols(obj)
        if (symKeys.length) {
            symKeys.forEach(symKey => {
                newobj[symKey] = cloneShallow(obj[symKey], hash)
           })
        }
        for (let key in obj) {
             if (obj.hasOwnProperty(key)) {
                 newobj[key] = deepClone(obj[key], hash)
             }
         }
         return newobj
    }
    else {
        return obj
    }
}
let b = {}
let originArray = [1, [2, 3], [4, 5], {a: b}];
b['a'] = originArray
console.log(originArray) // [1, [2, 3], [4, 5], {a: [1, [2, 3], [4, 5], {...}]}] 无限套娃
console.log(cloneArray) // [10, [20, 30], [4, 5, 'new'], {a: 60}]

例子

let obj = {
            a: 1,
            b: [1, 2],
            c: {d: 1},
            [Symbol('d')]: 1,
            e: Symbol('1'),
            f: undefined,
            g: function(){}
        }
3.5 迭代解决循环引用

将待拷贝的对象放入栈中,循环直至栈为空,解决了递归方法的爆栈问题
https://blog.csdn.net/qq_39989929/article/details/100065835

4 总结

赋值浅拷贝深拷贝
和原数据类型是否指向同一对象
第一层数据为基本数据类型改变会使原数据一同修改改变不会使原数据同一修改改变不会使原数据同一修改
原数据第一层中包含对象改变会使原数据一同修改改变会使深于第一层的数据修改改变不会使原数据同一修改

参考:https://github.com/axuebin/articles/issues/20
https://github.com/wind-jyf/blog/issues/1
循环引用
https://github.com/sqshada/Study-day/issues/2

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值