浅谈JavaScript浅拷贝和深拷贝


Posted in Javascript onNovember 07, 2021

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

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浅拷贝和深拷贝的文章就介绍到这了,更多相关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

Javascript 相关文章推荐
JavaScript 常用函数
Dec 30 Javascript
javascript:void(0)的作用示例介绍
Oct 28 Javascript
JavaScript获取多个数组的交集简单实例
Nov 11 Javascript
扩展bootstrap的modal模态框-动态添加modal框-弹出多个modal框
Feb 21 Javascript
解决ztree搜索中多级菜单展示不全问题
Jul 05 Javascript
浅谈Webpack自动化构建实践指南
Dec 18 Javascript
js验证身份证号码记录的方法
Apr 26 Javascript
JavaScript封闭函数及常用内置对象示例
May 13 Javascript
详解vue2.0模拟后台json数据
May 16 Javascript
详解微信小程序动画Animation执行过程
Sep 23 Javascript
ant design 日期格式化的实现
Oct 27 Javascript
Vue实现省市区三级联动
Dec 27 Vue.js
JavaScript严格模式不支持八进制的问题讲解
Javascript使用integrity属性进行安全验证
Nov 07 #Javascript
JavaScript中时间格式化新思路toLocaleString()
Nov 07 #Javascript
JavaScript中isPrototypeOf函数
JavaScript原型链详解
JavaScript组合继承详解
详细聊聊vue中组件的props属性
You might like
一个阿拉伯数字转中文数字的函数
2006/10/09 PHP
如何实现给定日期的若干天以后的日期
2006/10/09 PHP
PHP生成二维码的两个方法和实例
2014/07/01 PHP
身份证号码前六位所代表的省,市,区, 以及地区编码下载
2007/04/12 Javascript
Jquery中获取iframe的代码
2011/01/11 Javascript
js获取某元素的class里面的css属性值代码
2014/01/16 Javascript
JQuery基础语法小结
2015/02/27 Javascript
jQuery表格插件datatables用法汇总
2016/03/29 Javascript
Bootstrap路径导航与分页学习使用
2017/02/08 Javascript
Vue.js基础指令实例讲解(各种数据绑定、表单渲染大总结)
2017/07/03 Javascript
Express系列之multer上传的使用
2017/10/27 Javascript
vue组件与复用详解
2018/04/08 Javascript
jQuery UI实现动画效果代码分享
2018/08/19 jQuery
jQuery解析json格式数据示例
2018/09/01 jQuery
Vue Cli3 创建项目的方法步骤
2018/10/15 Javascript
jQuery实现动态添加和删除input框实例代码
2019/03/26 jQuery
vue滚动插件better-scroll使用详解
2019/10/18 Javascript
JS实现导航栏楼层特效
2020/01/01 Javascript
vue从后台渲染文章列表以及根据id跳转文章详情详解
2020/12/14 Vue.js
用Python登录Gmail并发送Gmail邮件的教程
2015/04/17 Python
Python将图片批量从png格式转换至WebP格式
2020/08/22 Python
详解Python之数据序列化(json、pickle、shelve)
2017/03/30 Python
使用python语言,比较两个字符串是否相同的实例
2018/06/29 Python
Python补齐字符串长度的实例
2018/11/15 Python
python实现简单多人聊天室
2018/12/11 Python
python3.6使用urllib完成下载的实例
2018/12/19 Python
Django xadmin安装及使用详解
2020/10/26 Python
CSS3 开发工具收集
2010/04/17 HTML / CSS
VICHY薇姿英国官网:全球专业敏感肌护肤领先品牌
2017/07/04 全球购物
平面网站制作专科生的自我评价分享
2013/12/11 职场文书
加入学生会演讲稿
2014/04/24 职场文书
党员剖析材料范文
2014/09/30 职场文书
优秀班主任材料
2014/12/16 职场文书
2015年大学组织委员个人工作总结
2015/10/23 职场文书
Redis实现分布式锁的五种方法详解
2022/06/14 Redis
webpack介绍使用配置教程详解webpack介绍和使用
2022/06/25 Javascript