一文彻底弄懂深拷贝

502 阅读2分钟

这是我参与 8 月更文挑战的第 23 天,活动详情查看: 8月更文挑战

前言

这是一道非常经典的面试题,而且似乎每个前端都能答出点什么,比如我们可以这么问:

  1. 请问了解过浅拷贝和深拷贝吗?
  2. 能大概说一下深拷贝的实现思路吗?
  3. 能用代码实现一下深拷贝吗?
  4. 遇到一些特殊类型的时候该怎么处理?比如函数、日期
  5. 数据量非常大的时候,会有什么问题呢? ...

列举的问题都是循序渐进的,如果前面没答出来,就下一题吧。。。

请问了解过浅拷贝和深拷贝吗?

这题相当于送分题,直接公布答案:

  • 浅拷贝只复制指向某个对象的指针,而不复制对象本身,相当于是新建了一个对象,该对象复制了原对象的指针,新旧对象还是共用一个内存块
  • 深拷贝是新建一个一模一样的对象,该对象与原对象共享内存,修改新对象也不会影响原对象

简单来说,浅拷贝只拷贝第一层对象,相当于一个壳,里面套的还是旧对象,而深拷贝则是完成拷贝一份全新的对象。

能大概说一下深拷贝的实现思路吗?

想要实现深拷贝,我们必须得先弄明白浅拷贝,因为深拷贝是递归+拷贝的思路。

浅拷贝的实现方式

  1. Object.assign()
    function cloneObj(obj) {
        return Object.assign({},obj)
    }

Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。

  1. Array.prototype.slice()
    function cloneArr(arr) {
        return arr.slice()
    }

slice() 方法返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括end)。原始数组不会被改变。

  1. Array.prototype.concat()
    function cloneArr(arr) {
        return arr.concat()
    }

concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。

  1. 解构赋值
    function cloneObj(obj) {
        return {...arr}
    }
    function cloneArr(arr) {
        return [...arr]
    }

能用代码实现一下深拷贝吗?

有了上面的基础,我们来实现一版本深拷贝

    function deepClone(obj) {
        if (Array.isArray(obj)) {
            return obj.map(item => deepClone(deepClone))
        }
        if (typof obj === 'object' && obj !== null) {
            const _obj = {}
            for(var i in obj){  
                _obj[i] = deepClone(obj[i])
            }
            return _obj
        }
        return obj
    }

小结

对于开篇提到的【数据量非常大的时候,会有什么问题呢?】,这是一个开放性的问题。一般该问题的方向如下:

  1. 递归会导致函数调用栈无法释放,最终爆栈
    • 解决思路:1. 用数据模拟函数调用栈;2. 尾递归优化
  2. 执行时间过长
    • 解决思路:1. 分析数据结构加入优化策略;2.分片执行;3.并发模式