浅谈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 相关文章推荐
Js之软键盘实现(js源码)
Jan 30 Javascript
Javascript中的数学函数集合
May 08 Javascript
汉化英文版的Dreamweaver CS5并自动提示jquery
Nov 25 Javascript
Jquery解析Json格式数据过程代码
Oct 17 Javascript
详谈jQuery操纵DOM元素属性 attr()和removeAtrr()方法
Jan 22 Javascript
分享9点个人认为比较重要的javascript 编程技巧
Apr 27 Javascript
JavaScript设置、获取、清除单值和多值cookie的方法
Nov 17 Javascript
浏览器调试动态js脚本的方法(图解)
Jan 19 Javascript
js中Array对象的常用遍历方法详解
Jan 17 Javascript
微信小程序利用云函数获取手机号码
Dec 17 Javascript
Vue路由权限控制解析
Nov 09 Javascript
vue首次渲染全过程
Apr 21 Vue.js
JavaScript严格模式不支持八进制的问题讲解
Javascript使用integrity属性进行安全验证
Nov 07 #Javascript
JavaScript中时间格式化新思路toLocaleString()
Nov 07 #Javascript
JavaScript中isPrototypeOf函数
JavaScript原型链详解
JavaScript组合继承详解
详细聊聊vue中组件的props属性
You might like
php_pdo 预处理语句详解
2016/11/21 PHP
php+webSoket实现聊天室示例代码(附源码)
2017/02/17 PHP
PHP实现的简单排列组合算法应用示例
2017/06/20 PHP
thinkphp5 框架结合plupload实现图片批量上传功能示例
2020/04/04 PHP
TP5框架实现自定义分页样式的方法示例
2020/04/05 PHP
JS打开层/关闭层/移动层动画效果的实例代码
2013/05/11 Javascript
jQuery 顶部导航跟随滚动条滚动固定浮动在顶部
2014/06/06 Javascript
JS在可编辑的div中的光标位置插入内容的方法
2014/11/20 Javascript
jQuery css() 方法动态修改CSS属性
2016/09/25 Javascript
基于JS实现横线提示输入验证码随验证码输入消失(js验证码的实现)
2016/10/27 Javascript
JS类的定义与使用方法深入探索
2016/11/26 Javascript
Form表单上传文件(type="file")的使用
2017/08/03 Javascript
BootStrap模态框不垂直居中的解决方法
2017/10/19 Javascript
浅谈vue 单文件探索
2018/09/05 Javascript
Vue移动端右滑屏幕返回上一页附源码下载
2019/06/26 Javascript
JS Ajax请求会话过期处理问题解决方法分析
2019/11/16 Javascript
javascript实现倒计时提示框
2021/03/02 Javascript
[02:17]《辉夜杯》TRG战队巡礼
2015/10/26 DOTA
[01:12:08]LGD vs OG 2019国际邀请赛淘汰赛 胜者组 BO3 第一场 8.24
2019/09/10 DOTA
python回溯法实现数组全排列输出实例分析
2015/03/17 Python
使用Python的Flask框架构建大型Web应用程序的结构示例
2016/06/04 Python
Python绘制七段数码管实例代码
2017/12/20 Python
使用requests库制作Python爬虫
2018/03/25 Python
python判断文件夹内是否存在指定后缀文件的实例
2019/06/10 Python
解决Python pip 自动更新升级失败的问题
2020/02/21 Python
Pandas的数据过滤实现
2021/01/15 Python
使用css3 属性如何丰富图片样式(圆角 阴影 渐变)
2012/11/22 HTML / CSS
美国著名的户外用品品牌:L.L.Bean
2018/01/05 全球购物
九年级英语教学反思
2014/01/31 职场文书
负责人任命书范本
2014/06/04 职场文书
卫生厅领导班子党的群众路线教育实践活动整改措施
2014/09/20 职场文书
工作检讨书怎么写
2014/10/10 职场文书
房地产置业顾问岗位职责
2015/04/11 职场文书
详解PHP服务器如何在有限的资源里最大提升并发能力
2021/05/25 PHP
CSS实现切角+边框+投影+内容背景色渐变效果
2021/11/01 HTML / CSS
Go 内联优化让程序员爱不释手
2022/06/21 Golang