【前端面经】js-深浅拷贝

整理前理解口述

深浅拷贝问题的出现是js对不同类型的存储方式而引发的;

对于原始数据类型, 它们的值是直接存储在栈内存中; 而复杂数据类型, 栈内存中记录的是它的指针, 而指针指向堆内存中真正的值;

所以对于原始数据类型就没有深浅拷贝一说;

而对于复杂数据类型, 浅拷贝就是仅复制指针, 但被复制对象改变时, 新复制的对象也会跟着改变; 深拷贝则是连同堆内存中的数据完全拷贝一份, 新旧对象的变化互不影响;

什么是深浅拷贝

浅拷贝:拷贝复杂类型时,仅拷贝对象的指针;当原对象改变时,拷贝对象也会跟着改变

深拷贝:拷贝复制类型时,拷贝对象的值;当原对象改变时,拷贝对象不会跟着改变

深浅拷贝的实现方案

浅拷贝方案:

  1. …展开运算符(ES6)
  2. obj.assign()
  3. contact

深拷贝:

  1. 手写递归
  2. JSON.parse(JSON.stringfy())
    • 先转换成字符串, 再解析回obj
    • 缺点是不能处理正则\函数等特殊数据类型
  3. 一些库封装的深拷贝方法, 例: loadsh的_.cloneDeep()

手写实现

// 先写一个浅拷贝
function _deepClone(target) {
    let cloneRes = {};
    for(const key in target) {
        cloneRes[key] = target[key]
    }
    return cloneRes
    // 函数出口
    // if (typeof target !== 'obejct') return target
}

// 2. 普通深拷贝(只考虑对象/非对象)
function _deepClone(target) {
    if (typeof target === 'object'){
        // 如果是对象, 进行处理
        let cloneRes = {};
        for(const key in target) {
            cloneRes[key] = _deepClone(target[key])
        }
        return cloneRes
    } else {
        // 如果不是对象, 直接返回
        return target
    }
}

// 3.考虑数组的深拷贝
function _deepClone(target) {
    if (typeof target === 'object') {
        let cloneRes = Array.isArray(target) ? [] : {}; // 核心
        for (const key in target) {
            cloneRes[key] = _deepClone(target[key])
        }
        return cloneRes
    } else {
        return target
    }
}

// 4. 考虑循环引用
// Q:为什么要用WeakMap替代Map?
// A:强引用和弱引用的区别,强引用只能手动释放,弱引用能够被垃圾回收机制释放,使用Map会造成内存额外消耗。
//    WeakMap中的键是弱引用,Map中的键是强引用
function _deepClone(target, map=new WeakMap()) {
    if (typeof target === 'object') {
        let cloneRes = Array.isArray(target) ? [] : {}; // 核心
        // 防止循环调用导致的栈内存溢出
        if (map.get(target)) {
            return map.get(target)
        }
        map.set(target, cloneRes)
        for (const key in target) {
            cloneRes[key] = _deepClone(target[key], map)
        }
        return cloneRes
    } else {
        return target
    }
}

// 5. 考虑其他数据类型
function _deepClone (target, map = new WeakMap()) {
    if (target === null) return null
    if (target instanceof Date) return new Date(target)
    if (target instanceof RegExp) return new RegExp(target);
    if (typeof target !== 'object') return target
    if (map.get(target)) return map.get(target);

    let cloneRes = new target.constructor()
    map.set(target, cloneRes)
    for (const key in target) {
        if (target.hasOwnProperty(key)) {
            // map.set(key, target[key])
            cloneRes[key] = _deepClone(target[key], map);
        }
    }
    return cloneRes
}
// 6. 性能优化
// 把for..in改为普通的for循环,性能会有所提高

// 测试案例
const target = {
    field1: 1,
    field2: undefined,
    field3: {
        child: 'child'
    },
    field4: [2, 4, 8]
};
const newTarget = _deepClone(target)
newTarget.field4[1] = "Child2"
console.log(target);
console.log(newTarget);
console.log(target === newTarget);
// 输出:
// {
//   field1: 1,
//   field2: undefined,
//   field3: { child: 'child' },
//   field4: [ 2, 4, 8 ]
// }
// {
//   field1: 1,
//   field2: undefined,
//   field3: { child: 'child' },
//   field4: [ 2, 'Child2', 8 ]
// }
// false

参考链接&&相关文章

前端早读课【第2810期】JavaScript 深拷贝性能分析

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深海大凤梨_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值