浅谈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刷新站IP和PV
Sep 05 Javascript
改善你的jQuery的25个步骤 千倍级效率提升
Feb 11 Javascript
JavaScript对象链式操作代码(jquery)
Jul 04 Javascript
js阻止事件追加的具体实现
Oct 15 Javascript
JavaScript替换当前页面的方法
Apr 03 Javascript
关于JS中的方法是否加括号的问题
Jul 27 Javascript
实例详解jQuery的无new构建
Aug 02 Javascript
微信小程序 石头剪刀布实例代码
Jan 04 Javascript
jQuery EasyUI 折叠面板accordion的使用实例(分享)
Dec 25 jQuery
jquery ajax加载数据前台渲染方式 不用for遍历的方法
Aug 09 jQuery
django使用channels2.x实现实时通讯
Nov 28 Javascript
关于小程序优化的一些建议(小结)
Dec 10 Javascript
JavaScript严格模式不支持八进制的问题讲解
Javascript使用integrity属性进行安全验证
Nov 07 #Javascript
JavaScript中时间格式化新思路toLocaleString()
Nov 07 #Javascript
JavaScript中isPrototypeOf函数
JavaScript原型链详解
JavaScript组合继承详解
详细聊聊vue中组件的props属性
You might like
用js进行url编码后用php反解以及用php实现js的escape功能函数总结
2010/02/08 PHP
深入解析PHP的引用计数机制
2013/06/14 PHP
PHP实现的简易版图片相似度比较
2015/01/07 PHP
yii2的restful api路由实例详解
2019/05/14 PHP
JQuery 表格操作(交替显示、拖动表格行、选择行等)
2009/07/29 Javascript
javascript客户端解决方案 缓存提供程序
2010/07/14 Javascript
javascript 45种缓动效果 非常酷
2011/06/28 Javascript
js代码实现随机颜色的小方块
2015/07/30 Javascript
jQuery+PHP+MySQL二级联动下拉菜单实例讲解
2015/10/27 Javascript
jQuery内容筛选选择器实例代码
2017/02/06 Javascript
js学习总结_选项卡封装(实例讲解)
2017/07/13 Javascript
ES6中的Promise代码详解
2017/10/09 Javascript
vue源码学习之Object.defineProperty对象属性监听
2018/05/30 Javascript
vue里面使用mui的弹出日期选择插件实例
2018/09/16 Javascript
element-ui upload组件多文件上传的示例代码
2018/10/17 Javascript
如何在selenium中使用js实现定位
2020/08/18 Javascript
原生js实现拖拽移动与缩放效果
2020/08/24 Javascript
Python科学计算环境推荐——Anaconda
2014/06/30 Python
30秒轻松实现TensorFlow物体检测
2018/03/14 Python
python3.7简单的爬虫实例详解
2019/07/08 Python
python 字符串常用函数详解
2019/09/11 Python
Python urlencode和unquote函数使用实例解析
2020/03/31 Python
Python下使用Trackbar实现绘图板
2020/10/27 Python
一张图片能隐含千言万语之隐藏你的程序代码
2012/12/13 HTML / CSS
Ray-Ban雷朋奥地利官网:全球领先的太阳眼镜品牌
2020/10/12 全球购物
限量版运动鞋和街头服饰:TheDrop
2020/09/06 全球购物
OnePlus加拿大官网:中国国际化手机品牌
2020/10/13 全球购物
财务会计专业应届毕业生求职信
2013/10/18 职场文书
简历自我评价模版
2014/01/31 职场文书
社会公德演讲稿
2014/05/20 职场文书
《孙子兵法》:欲成大事者,需读懂这些致胜策略
2019/08/23 职场文书
创业计划书之香辣虾火锅
2019/09/23 职场文书
在JavaScript中如何使用宏详解
2021/05/06 Javascript
python迷宫问题深度优先遍历实例
2021/06/20 Python
Go 语言中 20 个占位符的整理
2021/10/16 Golang
centos8安装MongoDB的详细过程
2021/10/24 MongoDB