浅谈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架构javascript基础体系
Jan 01 Javascript
JavaScript高级程序设计 XML、Ajax 学习笔记
Sep 10 Javascript
html中使用javascript调用本地程序(exe、doc等)实现代码
Apr 26 Javascript
JS实现匀速运动的代码实例
Nov 29 Javascript
JavaScript设计模式之原型模式(Object.create与prototype)介绍
Dec 28 Javascript
AngularJS中watch监听用法分析
Nov 04 Javascript
AngularJS指令中的绑定策略实例分析
Dec 14 Javascript
激动人心的 Angular HttpClient的源码解析
Jul 10 Javascript
解决vue中修改export default中脚本报一大堆错的问题
Aug 27 Javascript
JS中准确判断变量类型的方法
Jun 01 Javascript
在vue-cli创建的项目中使用sass操作
Aug 10 Javascript
JavaScript中layim之整合右键菜单的示例代码
Feb 06 Javascript
JavaScript严格模式不支持八进制的问题讲解
Javascript使用integrity属性进行安全验证
Nov 07 #Javascript
JavaScript中时间格式化新思路toLocaleString()
Nov 07 #Javascript
JavaScript中isPrototypeOf函数
JavaScript原型链详解
JavaScript组合继承详解
详细聊聊vue中组件的props属性
You might like
PHP输出缓存ob系列函数详解
2014/03/11 PHP
PHP经典面试题之设计模式(经常遇到)
2015/10/15 PHP
php连接mysql数据库
2017/03/21 PHP
利用PHP实现一个简单的用户登记表示例
2017/04/25 PHP
javascript innerHTML使用分析
2010/12/03 Javascript
jQuery EasyUI API 中文文档 - Tree树使用介绍
2011/11/19 Javascript
深入理解JQuery keyUp和keyDown的区别
2013/12/12 Javascript
node.js中的console用法总结
2014/12/15 Javascript
JavaScript Math.floor方法(对数值向下取整)
2015/01/09 Javascript
基于BootStrap Metronic开发框架经验小结【三】下拉列表Select2插件的使用
2016/05/12 Javascript
Nodejs进阶:基于express+multer的文件上传实例
2016/11/21 NodeJs
BootStrap 下拉菜单点击之后不会出现下拉菜单(下拉菜单不弹出)的解决方案
2016/12/14 Javascript
js实现文字跑马灯效果
2017/02/23 Javascript
js提取中文拼音首字母的封装工具类
2018/03/12 Javascript
Bootstrap 模态框自定义点击和关闭事件详解
2018/08/10 Javascript
ES2020 已定稿,真实场景案例分析
2020/05/25 Javascript
微信小程序基于高德地图API实现天气组件(动态效果)
2020/10/22 Javascript
Python高效编程技巧
2013/01/07 Python
Python可跨平台实现获取按键的方法
2015/03/05 Python
Windows下实现Python2和Python3两个版共存的方法
2015/06/12 Python
Python 实现简单的电话本功能
2015/08/09 Python
浅谈Python 对象内存占用
2016/07/15 Python
Python中的wordcloud库安装问题及解决方法
2020/05/27 Python
Flask缓存静态文件的具体方法
2020/08/02 Python
详解使用CSS3的@media来编写响应式的页面
2017/11/01 HTML / CSS
html5 canvas 使用示例
2010/10/22 HTML / CSS
检测浏览器对HTML5和CSS3支持度的方法
2015/06/25 HTML / CSS
HTML5拖放效果的实现代码
2016/11/17 HTML / CSS
美国最大的无人机经销商:DroneNerds
2018/03/20 全球购物
澳大利亚领先的女帽及配饰公司:Morgan&Taylor
2019/12/01 全球购物
成品仓管员岗位职责
2013/12/11 职场文书
幼儿教师自我剖析材料
2014/09/29 职场文书
毕业生个人自荐书
2015/03/05 职场文书
2016最新离婚协议书范本及程序
2016/03/18 职场文书
销区经理年终述职报告模板
2019/11/28 职场文书
MySQL数据库查询之多表查询总结
2022/08/05 MySQL