浅谈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 写类方式之八
Jul 05 Javascript
extjs 学习笔记(一) 一些基础知识
Oct 13 Javascript
浅谈Unicode与JavaScript的发展史
Jan 19 Javascript
浅谈JavaScript中setInterval和setTimeout的使用问题
Aug 01 Javascript
基于jQuery实现音乐播放试听列表
Apr 14 Javascript
js Canvas实现的日历时钟案例分享
Dec 25 Javascript
浅谈js停止事件冒泡 阻止浏览器的默认行为(阻止超连接 #)
Feb 08 Javascript
javascript 使用正则test( )第一次是 true,第二次是false
Feb 22 Javascript
深入对Vue.js $watch方法的理解
Mar 20 Javascript
vue实现留言板todolist功能
Aug 16 Javascript
AngularJS对动态增加的DOM实现ng-keyup事件示例
Mar 12 Javascript
vue实现把接口单独存放在一个文件方式
Aug 13 Javascript
JavaScript严格模式不支持八进制的问题讲解
Javascript使用integrity属性进行安全验证
Nov 07 #Javascript
JavaScript中时间格式化新思路toLocaleString()
Nov 07 #Javascript
JavaScript中isPrototypeOf函数
JavaScript原型链详解
JavaScript组合继承详解
详细聊聊vue中组件的props属性
You might like
纯php打造的tab选项卡效果代码(不用js)
2010/12/29 PHP
PHP第一季视频教程(李炎恢+php100 不断更新)
2011/05/29 PHP
WordPress中获取所使用的模板的页面ID的简单方法
2015/12/31 PHP
PHP数字前补0的自带函数sprintf 和number_format的用法(详解)
2017/02/06 PHP
php检测mysql表是否存在的方法小结
2017/07/20 PHP
thinkPHP框架中执行事务的方法示例
2018/05/31 PHP
PHP hex2bin()函数用法讲解
2019/02/25 PHP
PHP实现微信退款的方法示例
2019/03/26 PHP
PHP copy函数使用案例代码解析
2020/09/01 PHP
JavaScript 对话框和状态栏使用说明
2009/10/25 Javascript
javascript整除实现代码
2010/11/23 Javascript
JS实现为表格动态添加标题的方法
2015/03/31 Javascript
用js编写的简单的计算器代码程序
2015/08/04 Javascript
Jquery跨域获得Json的简单实例
2016/05/18 Javascript
jquery easyui datagrid实现增加,修改,删除方法总结
2016/05/25 Javascript
浅谈js里面的InttoStr和StrtoInt
2016/06/14 Javascript
简单谈谈原生js的math对象
2017/06/27 Javascript
解决三元运算符 报错“SyntaxError: can''t assign to conditional expression”
2020/02/12 Javascript
python k-近邻算法实例分享
2014/06/11 Python
轻松掌握python设计模式之访问者模式
2016/11/18 Python
python中urlparse模块介绍与使用示例
2017/11/19 Python
浅谈python数据类型及类型转换
2017/12/18 Python
python 在某.py文件中调用其他.py内的函数的方法
2019/06/25 Python
python Django 创建应用过程图示详解
2019/07/29 Python
浅谈django2.0 ForeignKey参数的变化
2019/08/06 Python
简单了解Python3 bytes和str类型的区别和联系
2019/12/19 Python
Python多进程multiprocessing、进程池用法实例分析
2020/03/24 Python
基于Python爬取51cto博客页面信息过程解析
2020/08/25 Python
HTML5+Canvas+CSS3实现齐天大圣孙悟空腾云驾雾效果
2016/04/26 HTML / CSS
Lookfantastic葡萄牙官方网站:欧洲第一大化妆品零售商
2018/03/17 全球购物
init进程的作用
2015/08/20 面试题
党的群众路线教育实践活动个人对照检查材料(医生)
2014/11/05 职场文书
和领导吃饭祝酒词
2015/08/11 职场文书
2015教师个人师德工作总结
2015/10/23 职场文书
2019年鼓励无偿献血倡议书
2019/09/17 职场文书
SQL Server连接查询的实用教程
2021/04/07 SQL Server