浅谈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第一课
Feb 27 Javascript
capacityFixed 基于jquery的类似于新浪微博新消息提示的定位框
May 24 Javascript
使用js写的一个简易的投票
Nov 27 Javascript
jQuery+CSS实现一个侧滑导航菜单代码
May 09 Javascript
js中let和var定义变量的区别
Feb 08 Javascript
JavaScript中使用import 和require打包后实现原理分析
Mar 07 Javascript
Vue下滚动到页面底部无限加载数据的示例代码
Apr 22 Javascript
在vue.js中使用JSZip实现在前端解压文件的方法
Sep 05 Javascript
JavaScript实现无限级递归树的示例代码
Mar 29 Javascript
浅谈对于react-thunk中间件的简单理解
May 01 Javascript
详解Vue2.5+迁移至Typescript指南
Aug 01 Javascript
express中static中间件的具体使用方法
Oct 17 Javascript
JavaScript严格模式不支持八进制的问题讲解
Javascript使用integrity属性进行安全验证
Nov 07 #Javascript
JavaScript中时间格式化新思路toLocaleString()
Nov 07 #Javascript
JavaScript中isPrototypeOf函数
JavaScript原型链详解
JavaScript组合继承详解
详细聊聊vue中组件的props属性
You might like
syphon 虹吸式咖啡冲泡冲煮倒水的得与失
2021/03/03 冲泡冲煮
解密ThinkPHP3.1.2版本之模板继承
2014/06/19 PHP
thinkPHP框架RBAC实现原理分析
2019/02/01 PHP
可以用来调试JavaScript错误的解决方案
2010/08/07 Javascript
关于JavaScript中原型继承中的一点思考
2012/07/25 Javascript
Jquery插件分享之气泡形提示控件grumble.js
2014/05/20 Javascript
js取得html iframe中的元素和变量值
2014/06/30 Javascript
JS动态加载脚本并执行回调操作
2016/08/24 Javascript
js实现4个方向滚动的球
2017/03/06 Javascript
vue脚手架及vue-router基本使用
2018/04/09 Javascript
r.js来合并压缩css文件的示例
2018/04/26 Javascript
Vue-router 中hash模式和history模式的区别
2018/07/24 Javascript
jQuery Ajax async=>false异步改为同步时,解决导致浏览器假死的问题
2019/07/22 jQuery
将RGB值转换为灰度值的简单算法
2019/10/09 Javascript
JavaScript如何借用构造函数继承
2019/11/06 Javascript
JS通过识别id、value值对checkbox设置选中状态
2020/02/19 Javascript
Angular+ionic实现折叠展开效果的示例代码
2020/07/29 Javascript
Python读写txt文本文件的操作方法全解析
2016/06/26 Python
python daemon守护进程实现
2016/08/27 Python
基于Python的文件类型和字符串详解
2017/12/21 Python
在PyCharm导航区中打开多个Project的关闭方法
2019/01/17 Python
Python批量生成特定尺寸图片及图画任意文字的实例
2019/01/30 Python
Python实现E-Mail收集插件实例教程
2019/02/06 Python
Python3.0中普通方法、类方法和静态方法的比较
2019/05/03 Python
Keras自动下载的数据集/模型存放位置介绍
2020/06/19 Python
纯CSS3编写的的精美动画进度条(无flash/无图像/无脚本/附源码)
2013/01/07 HTML / CSS
用HTML5制作视频拼图的教程
2015/05/13 HTML / CSS
英国美发和美容产品商城:HQhair
2019/02/08 全球购物
公司年会搞笑主持词
2014/03/24 职场文书
放飞梦想演讲稿
2014/05/05 职场文书
基层党建工作汇报材料
2014/08/15 职场文书
销售代理协议书
2014/09/30 职场文书
交通事故一次性赔偿协议书范本
2014/11/02 职场文书
学校学期工作总结
2015/08/13 职场文书
Python OpenCV快速入门教程
2021/04/17 Python
Python中常见的反爬机制及其破解方法总结
2021/06/10 Python