Skip to content

一、浅拷贝和深拷贝

  • 浅拷贝是创建一个新对象,这个新对象和原始对象的属性值一样。如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址,其中一个对象的地址值改变,另一个对象的值也会跟着改变。
  • 深拷贝是将对象从内存中完整的拷贝出来,从堆内存中在申请一个新的空间存放新对象。修改新对象不会影响原始对象。

二、对象赋值和深/浅拷贝的区别

  • 把一个对象赋值给一个新的变量时,赋值的其实是==对象在栈中的地址,而不是堆中的数据。==也就是两个对象指向的是同一个存储空间,无论哪个对象发生变化,改变的都是堆中存储空间的内容。所以两个对象是相通的。
  • 浅拷贝:重新在堆中创建内存,拷贝前后对象的基本数据类型互不影响,引用类型因为共享同一块内存,会互相影响。
  • 深拷贝:从堆内存中开辟一个新的空间,存放新的对象。对对象中的内从进行递归拷贝,拷贝前后的两个对象互相不影响。

三、浅拷贝实现方法

1. 对象浅拷贝

1.1 Object.assign()

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

js
let obj1 = {person:{name:'kole',age:21},sex:'男'}
let obj2 = Object.assign({},obj1)
obj2.person.name = 'xuebi'
obj2.sex = '女'
console.log(obj1)//{person:{name: "xuebi", age: 21}, sex: "男"}

1.2 扩展运算符

js
let obj1 = {person:{name:'kole',age:21},sex:'男'}
let obj2 = {...obj1}
obj2.person.age = 52
obj2.sex = '女'
console.log(obj1)//{person: {name: "kole", age: 52}, sex: "男"}
console.log(obj2)//{person: {name: "kole", age: 52}, sex: "女"}

2. 数组浅拷贝

Array.prototype.concat()

js
let arr = [1,2,{name:'kole'}]
let arr1 = arr.concat();
arr1[2].name = 'kaka'
arr1[0]=2
console.log(arr)//[1, 2,  {name: "kaka"}]
console.log(arr1)//[2, 2, {name: "kaka"}]

Array.prototype.slice()

js
let arr = [1,2,{name:'kole'}]
let arr1 = arr.slice();
arr1[2].name = 'kaka'
arr1[0]=2
console.log(arr)//[1, 2,  {name: "kaka"}]
console.log(arr1)//[2, 2, {name: "kaka"}]

三、深拷贝实现方法

  1. JSON.parse(JSON.stringify())
js
let arr = [1,2,{name:'kole'}]
let arr1 = JSON.parse(JSON.stringify(arr))
arr1[2].name = 'kaka'
arr1[0] = 2
console.log(arr)//[1, 2, {name: "kole"}]
console.log(arr1)// [2, 2, {name: "kaka"}]

这种方法虽然可以实现数组或对象的深拷贝,但不能处理函数和正则,因为这两者基于JSON.stringify和JSON.parse处理后,得到的正则就不再是正则(变为空对象),得到的函数就不再是函数(变为null)了。

js
let arr = [1,2,{name:'kole',age:null,sex:''},function clone (){console.log(123)}]
let arr1 = JSON.parse(JSON.stringify(arr))
console.log(arr1)
/*
0: 1
1: 2
2: {name: "kole", age: null, sex: ""}
3: null
length: 4
__proto__: Array(0)
*/
  1. 手写递归
js
function deepClone(obj) {
    if(obj == null || typeof obj !== 'object') {
        return obj
    }
    // 初始化返回结果
    let newObj
    if(obj instanceof Array) {
        newObj = []
    } else {
        newObj = {}
    }
    for(let key in obj) {
        // 保证key不是原型属性
        if(obj.hasOwnProperty(key)) {
            // 递归调用
            newObj[key] = deepClone(obj[key])
        }
    }
    return newObj
}

Released under the MIT License.