浅谈JavaScript浅拷贝和深拷贝

 更新时间:2021年11月05日 09:04:44   作者:快乐编程  
这篇文章主要介绍了浅谈JavaScript浅拷贝和深拷贝,javascript中的对象是引用类型,在复制对象的时候就要考虑是用浅拷贝还是用深拷贝。接下来一起聊聊吧,感兴趣的小伙伴也可以参考一下
(福利推荐:【腾讯云】服务器最新限时优惠活动,云服务器1核2G仅99元/年、2核4G仅768元/3年,立即抢购>>>:9i0i.cn/qcloud

(福利推荐:你还在原价购买阿里云服务器?现在阿里云0.8折限时抢购活动来啦!4核8G企业云服务器仅2998元/3年,立即抢购>>>:9i0i.cn/aliyun

网上关于这个话题,讨论有很多了,根据各路情况我自己整理了一下,最后还是能接近完美的实现深拷贝,欢迎大家讨论。

javascript中的对象是引用类型,在复制对象的时候就要考虑是用浅拷贝还是用深拷贝。

一、直接赋值

对象是引用类型,如果直接赋值给另外一个对象,那么只是赋值一个引用,实际上两个变量指向的同一个数据对象,如果其中一个对象的属性变更,那么另外一个也会变更。

示例1,简单示例:

let human1 = {
    id: 1,
    name: "happy"
};
human2 = human1; // 这里就是直接赋值
 
console.log(human1); // {id: 1, name: 'happy'}
console.log(human2); // {id: 1, name: 'happy'}
 
// 更改human1的名称,human2的也会更改
human1.name = "life";
console.log(human1); // {id: 1, name: 'life'}
console.log(human2); // {id: 1, name: 'life'}

示例2,对象作为参数传递也是传递引用:

let human1 = {
    id: 1,
    name: "happy"
};
 
console.log(human1); // {id: 1, name: 'happy'}
 
function foo(human) {
    // 这里更改了human对象的名称
    human.name = "life";
}
foo(human1); // 传递对象是传递引用
 
console.log(human1); // {id: 1, name: 'life'}

二、浅拷贝

浅拷贝只是复制了对象的第一层,如果第一层的属性值是对象,那么该属性也只是复制了一个引用。

let object1 = {
    a: 1,
    b: { // b是对象
        b1: 2
    }
};
object2 = Object.assign({}, object1); // 这里就是浅拷贝,其中的b对象只复制了引用
 
// a是常规类型,不会互相影响
object1.a = 10;
console.log(object1.a); // 10
console.log(object2.a); // 1
 
// b是对象,会互相影响
object1.b.b1 = 20;
console.log(object1.b.b1); // 20
console.log(object2.b.b1); // 20


如果要实现完整的复制,就要使用深拷贝。

三、深拷贝

森拷贝就是不光是一层要复制,里面的层如果是对象也要进行复制。

1. JSON对象的方式

如果对象可以确认是JSON对象,那么可以用JSON对象的方式。

沿用上面的例子:

let object1 = {
    a: 1,
    b: { // b是对象
        b1: 2
    }
};
 
object2 = JSON.parse(JSON.stringify(object1)); // 深拷贝
 
// a是常规类型,不会互相影响
object1.a = 10;
console.log(object1.a); // 10
console.log(object2.a); // 1
 
// b是对象,也不会互相影响
object1.b.b1 = 20;
console.log(object1.b.b1); // 20
console.log(object2.b.b1); // 2


这边深拷贝的原理其实就是先把对象转成json字符串,然后再转成json对象,中间转成json字符串后就和原来的对象没有关系了。

这方法的优点:实现非常简单。

缺点:

如果有属性值是函数的话,那么无法进行复制,数据会丢失。
另外原型对象无法进行复制。

所以这种方式只适合对象确认是一个纯粹的json数据。

2. 递归复制

因为需要一层一层递进复制,很容想到用递归的方式,参考如下实现:

function deepCopy(source) {
    // 如果不是对象或者是null则直接返回
    if (typeof source !== 'object' || source === null) {
        return source;
    }
 
    let target = {};
    // 遍历复制属性
    for (let k in source) {
        if (!source.hasOwnProperty(k)) {
            continue;
        }
 
        if (typeof source[k] === 'object') { // 如果是对象,则递归复制
            target[k] = deepCopy(source[k]);
            continue;
        }
 
        let descriptor = Object.getOwnPropertyDescriptor(source, k);
        Object.defineProperty(target, k, descriptor);
    }
 
    return target;
}

因为是一层一层复制,所以复制完成后,两个对象不会互相影响,并且也可以支持方法。

let object1 = {
    a: 1,
    b: { // b是对象
        b1: 2
    },
    f: function() { // f是方法
        console.log(3);
    }
};
object2 = deepCopy(object1); // 深拷贝,也可以复制函数了。
object1.f(); // 3
object2.f(); // 3
 
// b是对象,也不会互相影响
object1.b.b1 = 20;
console.log(object1.b.b1); // 20
console.log(object2.b.b1); // 2


复制原型对象

但是这个方法还存在一个问题,就是原型对象无法复制,稍微改进一下:

// 把 let target = {}; 改成如下方式
// 保证原型也进行了复制
let target = Object.create(Object.getPrototypeOf(source));


这样就可以了,来个示例验证一下:

function Human() {
    this.id = 1;
}
Human.prototype.bar = function() {
    console.log("bar");
};
 
let human1 = new Human();
human2 = deepCopy(human1);
 
console.log("human1", human1);
console.log("human2", human2);


查看下两个对象的原型:

深拷贝复制原型对象:

完美复制。

当然这样的方法也存在一个问题,就是递归本身存在如果层次过深,容易造成栈溢出的问题。但是在实务中也建议不要复制非常大的对象,应该有另外好的解决方法。

到此这篇关于浅谈JavaScript浅拷贝和深拷贝的文章就介绍到这了,更多相关JavaScript浅拷贝和深拷贝内容请搜索程序员之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持程序员之家!

参考文档:

JS实现深拷贝:https://www.cnblogs.com/dobeco/p/11295316.html
Object.assign():https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
Object.create():https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create
Object.getPrototypeOf():https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/getPrototypeOf
Object.defineProperty():https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
Object.getOwnPropertyDescriptor():https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor
hasOwnProperty():https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty

相关文章

  • 微信小程序支付之c#后台实现方法

    微信小程序支付之c#后台实现方法

    这篇文章主要介绍了微信小程序支付之c#后台实现方法的相关资料,希望通过本文能帮助到大家,让大家实现这样的功能,需要的朋友可以参考下
    2017-10-10
  • 前端框架ECharts?dataset对数据可视化的高级管理

    前端框架ECharts?dataset对数据可视化的高级管理

    这篇文章主要为大家介绍了前端框架ECharts?dataset对数据可视化的高级管理,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • 前端通过JavaScript创建修改CAD图形详情

    前端通过JavaScript创建修改CAD图形详情

    这篇文章介绍JavaScript创建修改CAD图形,创建修改CAD图形,一般是基于AutoCAD进行二次开发,ObjectARX是AutoDesk公司针对AutoCAD平台上的二次开发而推出的一个开发软件包,它提供了以C++为基础的面向对象的开发环境及应用程序接口,能真正快速的访问AutoCAD图形数据库
    2021-10-10
  • 微信小程序开发之相册选择和拍照详解及实例代码

    微信小程序开发之相册选择和拍照详解及实例代码

    这篇文章主要介绍了微信小程序开发之相册选择和拍照详解及实例代码的相关资料,需要的朋友可以参考下
    2017-02-02
  • 微信小程序 textarea 组件详解及简单实例

    微信小程序 textarea 组件详解及简单实例

    这篇文章主要介绍了微信小程序 textarea 组件详解及简单实例的相关资料,需要的朋友可以参考下
    2017-01-01
  • 文字幻灯片

    文字幻灯片

    文字幻灯片...
    2006-06-06
  • 前端算法题解leetcode36-有效的数独示例

    前端算法题解leetcode36-有效的数独示例

    这篇文章主要为大家介绍了前端算法题解leetcode36-有效的数独示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • JavaScript前端实用的工具函数封装

    JavaScript前端实用的工具函数封装

    这篇文章主要为大家介绍了JavaScript前端实用的一些工具函数的封装,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • js题解LeetCode1051 高度检查器哈希表对比

    js题解LeetCode1051 高度检查器哈希表对比

    这篇文章主要为大家介绍了JS题解LeetCode1051 高度检查器哈希表对比,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • JavaScript闭包closure详述

    JavaScript闭包closure详述

    这篇文章主要大家分享的是JavaScript闭包详述,在JavaScript部分,闭包是很重要的东西,所以我们今天就闭包的相关知识做一个总结,需要的朋友可以参考一下,希望对你有所帮助
    2021-12-12

最新评论

?


http://www.vxiaotou.com