1

1.介绍

深拷贝:拷贝前后没关系,改变拷贝后的值不会影响拷贝前的值

浅拷贝:拷贝前后有关系,拷贝的是引用,所以修改引用对象会影响。

对于数据类型,地址指针,存储方式还有不明白的建议看我这两篇文章
每日一面——仿写reverse方法
JS核心知识点梳理——变量篇

2.浅拷贝

2.1浅拷贝举例

我们先看一下扩展运算符...

let o = {name: 'fyy'}
let obj = {...o}
console.log(obj)//{name: 'fyy'}
obj.name = 1
console.log(o)//{name: 'fyy'}

如果对象只有一层,没有问题,但是一旦对象有两层

let o = {name: {firstname: 'f'}}
let obj = {...o}
console.log(obj)//{name: {firstname: 'f'}}
obj.name.firstname = 1
console.log(o)//{name: {firstname: 1}}

拷贝后的对象就会影响拷贝前的对象了
所以说展运算符...是浅拷贝

同理,object.assign concat slice 都是浅拷贝

浅拷贝的危害

写代码的时候,复制一个变量很常见,如果使用了浅拷贝,那么变量深层属性的访问就变成了多入口了。换句话说,可能我改变量A的某个属性,可能B变量的某个属性也变了。这将导致问题难以追踪,违反了单一性原则,所以我们应该避免浅拷贝。

浅拷贝原因

为什么会出现浅拷贝这种现象呢,因为上面的拷贝,如果属性值不是基础类型,拷贝的是地址,拷贝前后引用的同一个地址,所以能相互影响
比如{name:'fyy'} 如果name的属性值是基本类型,拷贝后的新创建一个该基础类型new String('fyy'),并指向它
但是如果是{name:{firstname:'f'}} 如果name的属性值是对象,则直接把原对象中name指向的地址给新对象中的name。

浅拷贝拷贝的是引用,指向的还是同一个对象。

3.深拷贝

我们保证每次递归拷贝都是新对象就能实现深拷贝。

JSON.parse(JSON.stringify())

第一种实现深拷贝的方法,但是如果对象里面有函数,这种方法不行

这种方法对于处理后台返回来的数据还是挺快的,瑕不掩瑜

递归实现

每次都生成一个新对象

function deppClone(obj) {
    if(obj == null ) return undefined
    // 如果是Date,RegExp实例,则从新返回一个这种实例
    if(obj instanceof Date) return new Date(obj)
    if(obj instanceof RegExp) return new RegExp(obj)
    //如果是基础类型,则返回基础类型,如果是方法则返回方法(因为方法里面不会有层级)
    if(typeof obj !== 'object') return obj
    //剩下的可嫩是数组或者是对象,返回相应的对象后者数组
    let newobj = new obj.constructor
    for(let key in obj) {
        if(obj.hasOwnProperty(key)){ //是自己的属性
            newobj[key] =deppClone(obj[key]) //但是可以是一个对象,所以还要递归一下
        }

    }
    return newobj
}

面试能写到这,基本高分通过了。
不过还有一个小问题
如果存在循环引用怎么办
obj.p = obj

循环引用

在遍历的时候p这个属性是对象obj,会触发deppClone方法,在遍历的时候p这个属性是对象obj,会触发deppClone方法.....无限循环了,所以已经deepclone了obj,再递归的时候我就不能再deepclone了obj 我需要将所有的深克隆的对象收集起来,拷贝之前进行对比,没有则拷贝,有的话就不用拷贝了,用之前已经拷贝过的

function deepClone(obj,hash=new WeakMap()) {
    if(obj == null ) return undefined
    // 如果是Date,RegExp实例,则从新返回一个这种实例
    if(obj instanceof Date) return new Date(obj)
    if(obj instanceof RegExp) return new RegExp(obj)
    //如果是基础类型,则返回基础类型,如果是方法则返回方法(因为方法里面不会有层级)
    if(typeof obj !== 'object') return obj
    if(hash.get(obj)) return  hash.get(obj)
    //剩下的可嫩是数组或者是对象,返回相应的对象后者数组
    let newobj = new obj.constructor
    hash.set(obj,newobj)
    for(let key in obj) {
        if(obj.hasOwnProperty(key)){ //是自己的属性
            newobj[key] =deepClone(obj[key],hash) //但是可以是一个对象,所以还要递归一下
        }

    }
    return newobj
}

Runningfyy
1.3k 声望661 粉丝