浅谈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 相关文章推荐
jquery中通过过滤器获取表单元素的实现代码
Jul 05 Javascript
关于js拖拽上传 [一个拖拽上传修改头像的流程]
Jul 13 Javascript
JS异常处理的一个想法(sofish)
Mar 14 Javascript
JS替换字符串中空格方法
Apr 17 Javascript
js实现简单的碰壁反弹效果
Aug 30 Javascript
简单谈谈Javascript函数中的arguments
Feb 09 Javascript
JS中SetTimeout和SetInterval使用初探
Mar 23 Javascript
让微信小程序支持ES6中Promise特性的方法详解
Jun 13 Javascript
echarts饼图扇区添加点击事件的实例
Oct 16 Javascript
微信小程序实现两个页面传值的方法分析
Dec 11 Javascript
js实现全选反选不选功能代码详解
Apr 24 Javascript
JS如何操作DOM基于表格动态展示数据
Oct 15 Javascript
JavaScript严格模式不支持八进制的问题讲解
Javascript使用integrity属性进行安全验证
Nov 07 #Javascript
JavaScript中时间格式化新思路toLocaleString()
Nov 07 #Javascript
JavaScript中isPrototypeOf函数
JavaScript原型链详解
JavaScript组合继承详解
详细聊聊vue中组件的props属性
You might like
不用GD库生成当前时间的PNG格式图象的程序
2006/10/09 PHP
PHP定时执行任务实现方法详解(Timer)
2015/07/30 PHP
简单了解将WordPress中的工具栏移到底部的小技巧
2015/12/31 PHP
理解 JavaScript 预解析
2009/10/25 Javascript
jQuery图片播放8款精美插件分享
2013/02/17 Javascript
JavaScript中把数字转换为字符串的程序代码
2013/06/19 Javascript
FireBug 调试JS入门教程 如何调试JS
2013/12/23 Javascript
jquery实现公告翻滚效果
2015/02/27 Javascript
javascript制作幻灯片(360度全景图片)
2015/07/28 Javascript
javascript数组去重的六种方法汇总
2015/08/16 Javascript
JavaScript实现搜索框的自动完成功能(一)
2016/02/25 Javascript
常用原生JS兼容性写法汇总
2016/04/27 Javascript
手把手教你搭建ES6的开发运行环境
2017/07/11 Javascript
总结js函数相关知识点
2018/02/27 Javascript
js实现每日签到功能
2018/11/29 Javascript
JS实现滚动条触底加载更多
2019/09/19 Javascript
vue项目中在可编辑div光标位置插入内容的实现代码
2020/01/07 Javascript
[55:44]完美世界DOTA2联赛决赛 FTD vs Phoenix 第二场 11.08
2020/11/11 DOTA
python刷投票的脚本实现代码
2014/11/08 Python
Python中字符编码简介、方法及使用建议
2015/01/08 Python
详解python的ORM中Pony用法
2018/02/09 Python
pandas使用apply多列生成一列数据的实例
2018/11/28 Python
python GUI库图形界面开发之PyQt5 MDI(多文档窗口)QMidArea详细使用方法与实例
2020/03/05 Python
Django 如何使用日期时间选择器规范用户的时间输入示例代码详解
2020/05/22 Python
详解Python设计模式之策略模式
2020/06/15 Python
如何利用Python写个坦克大战
2020/11/18 Python
numba提升python运行速度的实例方法
2021/01/25 Python
MAC Cosmetics官方网站:魅可专业艺术彩妆
2019/04/10 全球购物
制定岗位职责的原则
2013/11/08 职场文书
中专生自我鉴定范文
2014/02/02 职场文书
幼儿园元旦家长感言
2014/02/27 职场文书
电子信息工程自荐信
2014/05/26 职场文书
商场消防安全责任书
2014/07/29 职场文书
2014党的群众路线教育实践活动总结报告
2014/10/31 职场文书
学生逃课检讨书
2015/02/17 职场文书
业务员年终工作总结2015
2015/05/28 职场文书