前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >JS拷贝指南:浅拷贝与深拷贝详解

JS拷贝指南:浅拷贝与深拷贝详解

作者头像
FGGIT
发布2024-11-19 09:09:00
发布2024-11-19 09:09:00
40900
代码可运行
举报
文章被收录于专栏:知识学习知识学习
运行总次数:0
代码可运行

在JavaScript编程中,数据的复制是一个基础而又至关重要的概念,尤其在处理复杂的数据结构时,正确地执行拷贝操作可以避免意料之外的数据修改问题。JavaScript中的数据类型分为基本类型(如number、string、boolean等)和引用类型(如object、array、function等)。基本类型的值存储的是值本身,而引用类型存储的是指向该数据在内存中位置的引用。因此,对于引用类型而言,拷贝操作分为浅拷贝(Shallow Copy)和深拷贝(Deep Copy)两种情况。 浅拷贝:表面的复制 浅拷贝创建一个新的对象或数组,但它仅复制第一层的元素或属性,如果这些元素或属性是引用类型,则新旧对象将共享同一份引用。这意味着对拷贝后对象中引用类型属性的修改会影响到原对象。以下是一些实现浅拷贝的方法:

Object.create: 虽然主要用于实现原型继承,但它也可以视为一种特殊的浅拷贝,新对象的原型被设置为原对象。

代码语言:javascript
代码运行次数:0
运行
复制
let obj={
    a:1
}
let obj2=Object.create(obj)
obj.a=2
console.log(obj2.a);输出2

Object.assign: 将源对象的可枚举属性复制到目标对象,适合合并多个对象。

代码语言:javascript
代码运行次数:0
运行
复制
let obj={
    a:1,
    b:[1,2,3]
}
let obj1=Object.assign({},obj)
obj.b.push(4)
console.log(obj1);//输出{ a: 1, b: [ 1, 2, 3, 4 ] }

Array的concat、slice、解构赋值: 如 [].concat(arr)arr.slice(0)[…arr] , 这三个方法都会返回一个新的数组,[].concat(arr) 通过与空数组连接,arr.slice(0) 对数组从0开始分割,[…arr] 将数组解构重新赋值,都是对数组进行浅拷贝。

代码语言:javascript
代码运行次数:0
运行
复制
let arr=[1,2,3,{a:1}]
let arr1=arr.slice(0)
let arr2=[...arr]
let arr3=[].concat(arr)
arr[3].a=2
console.log(arr1);//[ 1, 2, 3, { a: 2 } ]
console.log(arr2);//[ 1, 2, 3, { a: 2 } ]
console.log(arr3);//[ 1, 2, 3, { a: 2 } ]

Array的toReversed().reverse(): arr.toReversed() 会将原数组倒序后返回一个新的数组,通过先反转再反转数组来实现浅拷贝,非直观但有效。

代码语言:javascript
代码运行次数:0
运行
复制
let arr=[1,2,3,{a:1}];
let arr2=arr.toReversed().reverse();
arr[3].a=2;
console.log(arr2);//输出[ 1, 2, 3, { a: 2 } ]

手搓浅拷贝

代码语言:javascript
代码运行次数:0
运行
复制
function shallowCopy(obj) {
    let newobj = {};
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            newobj[key] = obj[key];
        }
    }
    return newobj;
}
let obj = {
    a: 1,
    b: { n: 2 }
}
console.log(shallowCopy(obj));//{ a: 1, b: { n: 2 } }
obj.b.n=1
console.log(shallowCopy(obj));//{ a: 1, b: { n: 1 } }

自定义的 shallowCopy 函数通常会使用 for…in 循环遍历对象的属性,并利用 hasOwnProperty 检查属性是否属于对象本身,然后简单地复制这些属性到新对象中,不涉及深层次的递归。

深拷贝:彻底的复制

深拷贝不仅复制第一层的元素或属性,还会递归地复制所有层级的嵌套对象,确保原对象与拷贝对象之间完全独立,互不影响。深拷贝在需要完全隔离数据时非常关键。以下是几种常见的深拷贝方法:

JSON.parse(JSON.stringify(obj))

1:无法识别BigInt类型: 当对象中包含BigInt类型的值时,这个方法会将其转换为字符串,因为JSON标准不支持BigInt类型。因此,复制后的对象中的BigInt值不再是BigInt,而是字符串。

2:无法拷贝undefined、function、Symbol属性: (1):undefined的属性值会被忽略,因为它不是JSON格式的一部分。 (2):函数(function)作为对象的属性不能被序列化,所以在解析后会丢失。 (3):Symbol作为键或值同样不会被处理,因为JSON.stringify会忽略Symbol类型的键,且Symbol值也不能被直接序列化。

3:无法处理循环引用: 如果对象结构中存在循环引用(即对象A的某个属性引用了对象B,同时对象B的某个属性又引用了对象A),JSON.stringify 会抛出错误,因为它无法正确地序列化这样的结构。

代码语言:javascript
代码运行次数:0
运行
复制
let obj={
    a:1,
    b:{n:2},
    c:'cc',
    d:true,
    e:undefined,
    f:null,
    g:function(){},
    h:Symbol(1)
}

let newObj=JSON.parse(JSON.stringify(obj))
console.log(newObj)//{ a: 1, b: { n: 2 }, c: 'cc', d: true, f: null }
obj.b.n=1
console.log(newObj)//{ a: 1, b: { n: 2 }, c: 'cc', d: true, f: null }
//实现了深度拷贝,但是没有拷贝`undefined`、`function`、`Symbol`

structuredClone(obj)

是一个较新的API(在某些现代浏览器和Node.js中可用),它能完美地克隆大多数值,包括循环引用,但兼容性需考虑。

代码语言:javascript
代码运行次数:0
运行
复制
let obj={
    a:1,
    b:{n:1}
}

const newObj=structuredClone(obj);
obj.b.n=3
console.log(newObj);//{ a: 1, b: { n: 1 } }

自定义deepCopy函数

代码语言:javascript
代码运行次数:0
运行
复制
**let obj = {
    a: 1,
    b: { n: 2 },
    c: 'cc',
    d: true,
    e: undefined,
    f: null,
    g: function () { },
    h: Symbol(1),
    i: [1, 2, 3]
}
function deepCopy(obj) {
    let newObj = {}
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            //obj[key]是不是对象 typeof obj[key] === 'object'&& obj[key] !== null
            if (obj[key] instanceof Object) {
                newObj[key] = deepCopy(obj[key])
            } else {
                newObj[key] = obj[key]
            }
        }
    }
    return newObj
}
let obj2= deepCopy(obj);
console.log(obj2);
obj.i[0]=0
console.log(obj2);
/*输出结果:
{
  a: 1,
  b: { n: 2 },
  c: 'cc',
  d: true,
  e: undefined,
  f: null,
  g: {},
  h: Symbol(1),
  i: { '0': 1, '1': 2, '2': 3 }
}
{
  a: 1,
  b: { n: 2 },
  c: 'cc',
  d: true,
  e: undefined,
  f: null,
  g: {},
  h: Symbol(1),
  i: { '0': 1, '1': 2, '2': 3 }
}
*/**

实现一个深拷贝函数通常需要递归地检查每个属性,如果属性值是对象,则递归调用自身进行拷贝;否则,直接复制该属性值。这种方法灵活性高,可以处理更多特殊情况,但实现相对复杂。 总结

选择浅拷贝还是深拷贝,取决于具体的应用场景。如果数据结构简单,或者只需要复制顶层结构,浅拷贝提供了简洁高效的解决方案。而对于复杂的数据结构,特别是当内部包含多层嵌套或特殊类型的属性时,深拷贝能够确保数据的完整性和独立性。开发者应根据实际需求,权衡拷贝的深度与性能开销,灵活运用JavaScript提供的各种拷贝机制,确保程序的健壮性。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-11-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档