数组与对象的浅拷贝与深拷贝

博客主要介绍了浅拷贝和深拷贝的相关知识。浅拷贝只解决第一层问题,对于引用类型拷贝的是地址,介绍了对象和数组的多种浅拷贝方法;深拷贝会拷贝所有属性,指向动态分配内存,但存在忽略 undefined、symbol 等问题,还提及了深拷贝的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一 浅拷贝

浅拷贝: 当拷贝的数据是对象,对象里的
			1) 属性值是基本数据类型,那么拷贝的是地址, 
			2) 属性值是复杂引用类型,那么拷贝的是地址

浅拷贝只解决了第一层的问题,拷贝的第一层如果是引用类型,拷贝的还是地址

1.1 对象的浅拷贝

1.1.1 Object.assgin()方法

Object.assgin()

//将所有可以枚举属性的值从一个或多个源对象复制到目标对象,返回目标对象
let obj1 = {
  a : 1,
  b : {
       c : 2,
   },
   d: [3,4]
}

let obj = Object.assign({},obj1)

//修改obj里属性值为引用类型的值
obj.b.c = 3
obj.d[0] = 4
obj.d[1] = 5

console.log("obj",obj) 
console.log("obj1",obj1)

我们发现除了基本数据类型不变之外,引用类型的值都改变了,所以object.assgin()是浅拷贝,只解决了第一层.

在这里插入图片描述

1.1.2 对象展开运算符方法

let obj1 = {
        a : 1,
        b : {
            c : 2,
        },
        d: [3,4]
    }

    let obj = {...obj1}

    //修改obj里属性值为引用类型的值
    obj.a = 2
    obj.b.c = 3
    obj.d[0] = 4
    obj.d[1] = 5

    console.log("obj",obj) 
    console.log("obj1",obj1)

结果与上面是一样的:
在这里插入图片描述

1.2 数组的浅拷贝

数组的浅拷贝同样与对象一样,如果元素是基本数据类型拷贝的是值,是引用类型拷贝的是地址

1.2.1 slice()方法

let arrTest = [1,2,3,{a: 4},[5,6]]
    
//数组的浅拷贝
let arr = arrTest.slice(0) 
//slice()方法 第一个参数是开始的位置 第二个参数是结束的位置
// 不会影响原数组,返回新的截取数组
arr[1] = 3
arr[3].a = 5
arr[4][0] = 6

console.log("arrTest",arrTest)
console.log("arr",arr)

数组的slice拷贝遇到的问题与对象一致
在这里插入图片描述

1.2.2 concat()方法

let arrTest = [1,2,3,{a: 4},[5,6]]
    
//数组的浅拷贝
let arr = [].concat(arrTest)

arr[1] = 3
arr[3].a = 5
arr[4][0] = 6

console.log("arrTest",arrTest)
console.log("arr",arr)

得到的结果与上面一样,这里很多人会问是不是可以用数组的展开运算符,来拷贝数组,这种方法也是可行的.

//数组的展开运算符

let arrTest = [1,2,3,{a: 4},[5,6]]

let arr = [...arrTest]

在这里插入图片描述

1.3 实现浅拷贝

//判断是数组还是对象
function isObjectOrisArray(data) {
    if( Object.prototype.toString.call(data) == '[object Object]' ) {
        return 'object'
    } else if( Object.prototype.toString.call(data) == "[object Array]" ) {
        return 'array'
    }else {
        return false
    }

}

//浅拷贝实现
function cloneShallow(source) {

    //判断数据既不是是数组又不是对象
    if(!isObjectOrisArray(source)) return 

    //判断数据是数组还是对象
    let target = isObjectOrisArray(source) == 'object'? {} : []

    for(let key in source) {
        //避免遍历从原型链到继承下来的可遍历的属性
        if(Object.prototype.hasOwnProperty.call(source,key)) {
            target[key] = source[key]
        }
    }

    return target
}

测试数据:

Object.prototype.method=function(){
   console.log(this);
 }
 let sourceObj = {
     a: 1,
     b: 2,
     c: {
         d: 4
     }
 } 
 sourceObj.__proto__.f = 1
 let targetObj = cloneShallow(sourceObj)

 targetObj.c.d = 5

console.log(sourceObj,targetObj)

二 深拷贝

深拷贝会拷贝所有的属性,并指向的动态分配的内存,深拷贝相比于浅拷贝速度较慢并且花销较大,拷贝前后两个对象互不影响.

JSON.parse(JSON.stringfy(Object))
let obj1 = {
  a : 1,
  b : {
       c : 2,
   },
   d: [3,4]
}

let obj = JSON.parse(JSON.stringify(obj1))

//修改obj里属性值为引用类型的值
obj.b.c = 3
obj.d[0] = 4
obj.d[1] = 5

console.log("obj",obj) 
console.log("obj1",obj1)

这两个对象的就是相互对立
在这里插入图片描述

但是该方法有以下几个问题:

  1. 会忽略 undefined
  2. 会忽略 symbol
  3. 不能序列化函数
  4. 不能解决循环引用问题
  5. 不能正确处理new Date()
  6. 不能处理正则

undefined symbol 函数这三种情况,会直接忽略

2.2 实现深拷贝

function isObjectOrisArray(data) {
            
 if( Object.prototype.toString.call(data) == '[object Object]' ) {
      return 'object'
  } else if( Object.prototype.toString.call(data) == "[object Array]" ) {
      return 'array'
  }else {
      return false
  }
}

function cloneDeep(source,hash = new WeakMap()) {

    //判断数据既不是是数组又不是对象
    if(!isObjectOrisArray(source)) return 
    if(hash.has(source)) return hash.get(source)

    console.log(hash)

    //判断数据是数组还是对象
    let target = isObjectOrisArray(source) == 'object'? {} : []
    hash.set(source,target)
    
    for(let key in source) {
        //避免遍历从原型链到继承下来的可遍历的属性
        if(Object.prototype.hasOwnProperty.call(source,key)) {
            if(isObjectOrisArray(source[key])) {
                //递归写法
                target[key] = cloneDeep(source[key],hash)
            }else {
                target[key] = source[key]
            }
        }
        
    }

    return target
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值