js 赋值与浅、深拷贝

181 阅读2分钟

赋值、深/浅拷贝

概念

赋值:当我们吧一个对象赋值给一个新的变量时,赋的其实是该对象在栈中的地址,而不是堆中的数据。也就是两个对象指向的是同一个存储空间,其中任意一个发生变化,改动的都是同一个空间里面的内容

let ojb1 = {name:'web',age:11,likes:['run','sing']}
let obj2 = obj1
obj2.name = 'ken'
obj2.links[0] = 'runing'
console.log(obj1.name,obj1.links) // ken,['runing','sing']		

浅拷贝:重新在堆中创建内存空间,拷贝前后的基本数据类型互不影响,但是对象会相影响,因为拷贝前后对象里面的引用类型(对象)都是指向同一地址(跟赋值一样)。

let obj3 = {name:'清咔',likes:['run','read']}
let obj4 = {...obj3}
obj4.name = '沐夕花开'
obj4.likes[0] = 'coding'
console.log(obj4.name,obj4.likes) // 沐夕花开,['coding','read']

深拷贝:重新在堆中创建内存空间,拷贝前后所有数据类型都互不影响,因为会递归对对象拷贝。

let obj5 = {name:'清咔',likes:['run','read']}
let obj6 = DeepClone(obj5)
function DeepClone(obj){
    if(obj === null) return {}
    if(obj instanceof Date) return new Date(obj)
    if(obj instanceof RegExp) return new RegExp(obj)
    if(typeof obj !== 'object') return obj
    let _obj = new obj.constructor()
   	for(let key in obj){
        if(obj.hasOwnProperty(key)){
            _obj[key] = DeepClone(obj[key])
        }
    }
    return _obj
}
obj6.name = '沐夕花开'
obj6.likes[0] = 'coding'
console.log(obj5.name,obj5.likes) // 清咔,['run','read']
操作是否指向同一对象第一层数据为基本数据类型子对象
赋值源数据也会改变源数据也会改变
浅拷贝源数据不会改变源数据也会改变
深拷贝源数据不会改变源数据不会改变

实现

浅拷贝

  1. Object.assign()
  2. ES6展开运算符
  3. Array.prototype.concat()
  4. Array.prototype.slice()

深拷贝

  1. JSON.parse(JSON.stringify())

    利用JSON.stringify 将对象转换成JSON字符串,再通过JSON.parse()把字符串解析成对象来实现深拷贝,

    虽然可以实现深拷贝,但是会存在缺陷,不能处理正则和函数,因为通过JSON.stringify和JSON.parse处理后的正则会变成空对象,函数会变成null

  2. 各个类库的实现,如jquery的$.extend,lodash的_.cloneDeep

  3. 手写递归

    遍历对象、数组,直到里面的元素都是基本类型再去赋值,(有种特使情况:对象存在循环引用的情况,即对象的属性直接引用了自身的情况,解决的方法 可以开辟一个存储空间来存储当前对象和拷贝对象的关系,当需要拷贝当前对象时,根据存储空间判断有没有拷贝过这个对象,如果没有的话就继续拷贝,如果有的话就返回存储空间里面的值)

    实现步骤:

    1. 判断参数是否为空 => 返回 {}

    2. 判断参数是否为Date或者Reg类型=> 返回对应类型 (new Date/ new RegExp)

    3. 判断参数是否为Objec类型 => 不是的情况直接返回参数

    4. 创建新的对象(继承参数)

    5. 遍历新对象,调用自身函数

    6. 返回新对象