浅谈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 getBoundingClientRect() 来获取页面元素的位置
Nov 25 Javascript
js验证电话号码与手机支持+86的正则表达式
Jan 23 Javascript
js实现select跳转功能代码
Oct 22 Javascript
PhotoSwipe异步动态加载图片方法
Aug 25 Javascript
php输出全部gb2312编码内的汉字方法
Mar 04 Javascript
微信小程序 本地图片按照屏幕尺寸处理
Aug 04 Javascript
详解VUE项目中安装和使用vant组件
Apr 28 Javascript
JavaScript常用内置对象用法分析
Jul 09 Javascript
微信小程序实现原生步骤条
Jul 25 Javascript
vue 框架下自定义滚动条(easyscroll)实现方法
Aug 29 Javascript
微信小程序实现多行文字超出部分省略号显示功能
Oct 23 Javascript
Node.js创建一个Express服务的方法详解
Jan 06 Javascript
JavaScript严格模式不支持八进制的问题讲解
Javascript使用integrity属性进行安全验证
Nov 07 #Javascript
JavaScript中时间格式化新思路toLocaleString()
Nov 07 #Javascript
JavaScript中isPrototypeOf函数
JavaScript原型链详解
JavaScript组合继承详解
详细聊聊vue中组件的props属性
You might like
《PHP边学边教》(04.编写简易的通讯录――视频教程1)
2006/12/13 PHP
PHP开发过程中常用函数收藏
2009/12/14 PHP
解决更换PHP5.4以上版本后Dedecms后台登录空白问题的方法
2015/10/23 PHP
laravel自定义分页效果
2017/07/23 PHP
Yii2.0实现的批量更新及批量插入功能示例
2019/01/29 PHP
jQuery 版本的文本输入框检查器Input Check
2009/07/09 Javascript
为原生js Array增加each方法
2012/04/07 Javascript
JS实现图片横向滚动效果示例代码
2013/09/04 Javascript
JavaScript检查数字是否为整数或浮点数的方法
2015/06/09 Javascript
javascript实现确定和取消提示框效果
2015/07/10 Javascript
JS中的进制转换以及作用
2016/06/26 Javascript
让html元素随浏览器的大小自适应垂直居中的实现方法
2016/10/12 Javascript
bootstrap手风琴折叠示例代码分享
2017/05/22 Javascript
JavaScript实现的贝塞尔曲线算法简单示例
2018/01/30 Javascript
解决vue页面DOM操作不生效的问题
2018/03/17 Javascript
Vue通过ref父子组件拿值方法
2018/09/12 Javascript
angularjs中判断ng-repeat是否迭代完的实例
2018/09/12 Javascript
react实现同页面三级跳转路由布局
2019/09/26 Javascript
详解Nuxt.js 实战集锦
2019/11/19 Javascript
vue element-ui实现input输入框金额数字添加千分位
2019/12/29 Javascript
python+splinter自动刷新抢票功能
2018/09/25 Python
python 利用for循环 保存多个图像或者文件的实例
2018/11/09 Python
Python实现点阵字体读取与转换的方法
2019/01/29 Python
在win10和linux上分别安装Python虚拟环境的方法步骤
2019/05/09 Python
pandas DataFrame索引行列的实现
2019/06/04 Python
Python3 chardet模块查看编码格式的例子
2019/08/14 Python
python创建学生成绩管理系统
2019/11/22 Python
python datetime时间格式的相互转换问题
2020/06/11 Python
英国珠宝钟表和家居礼品精品店:David Shuttle
2018/02/24 全球购物
创业计划书的写作技巧及要点
2014/01/31 职场文书
租房协议书范本
2014/04/09 职场文书
孕妇离婚协议书范本
2014/11/20 职场文书
2015元旦联欢晚会结束语
2014/12/14 职场文书
一劳永逸彻底解决pip install慢的办法
2021/05/24 Python
Python 如何实现文件自动去重
2021/06/02 Python
redis 解决库存并发问题实现数量控制
2022/04/08 Redis