浅谈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 相关文章推荐
百度Popup.js弹出框进化版 拖拽小框架发布 兼容IE6/7/8,Firefox,Chrome
Apr 13 Javascript
js中设置元素class的三种方法小结
Aug 28 Javascript
jQuery基础框架浅入剖析
Dec 27 Javascript
返回页面顶部top按钮通过锚点实现(自写)
Aug 30 Javascript
可兼容IE的获取及设置cookie的jquery.cookie函数方法
Sep 02 Javascript
jquery toolbar与网页浮动工具条具体实现代码
Jan 12 Javascript
轻松实现jquery手风琴效果
Jan 14 Javascript
js HTML5上传示例代码完整版
Oct 10 Javascript
BootStrap实现带有增删改查功能的表格(DEMO详解)
Oct 26 Javascript
layer ui 导入文件之前传入数据的实例
Sep 23 Javascript
VUE:vuex 用户登录信息的数据写入与获取方式
Nov 11 Javascript
VueJS实现用户管理系统
May 29 Javascript
JavaScript严格模式不支持八进制的问题讲解
Javascript使用integrity属性进行安全验证
Nov 07 #Javascript
JavaScript中时间格式化新思路toLocaleString()
Nov 07 #Javascript
JavaScript中isPrototypeOf函数
JavaScript原型链详解
JavaScript组合继承详解
详细聊聊vue中组件的props属性
You might like
PHP中获取文件扩展名的N种方法小结
2012/02/27 PHP
php简单判断两个字符串是否相等的方法
2015/07/13 PHP
php 常用的系统函数
2017/02/07 PHP
Jquery 获取表单text,areatext,radio,checkbox,select值的代码
2009/11/12 Javascript
JavaScript 设计模式之组合模式解析
2010/04/09 Javascript
jquery入门——事件机制之事件中的冒泡现象示例解释
2020/09/12 Javascript
input:checkbox多选框实现单选效果跟radio一样
2014/06/16 Javascript
jquery处理页面弹出层查询数据等待操作实例
2015/03/25 Javascript
js实现将选中值累加到文本框的方法
2015/08/12 Javascript
JavaScript验证Email(3种方法)
2015/09/21 Javascript
JavaScript+CSS实现的可折叠二级菜单实例
2016/02/29 Javascript
jQuery实现的多滑动门,多选项卡效果代码
2016/03/28 Javascript
深入理解JavaScript单体内置对象
2016/06/06 Javascript
jQuery实现的选择商品飞入文本框动画效果完整实例
2016/08/10 Javascript
vue监听滚动事件实现滚动监听
2017/04/11 Javascript
EL表达式截取字符串的函数说明
2017/09/22 Javascript
基于Vue制作组织架构树组件
2017/12/06 Javascript
微信小程序实现图片压缩功能
2018/01/26 Javascript
详解vue.js下引入百度地图jsApi的两种方法
2018/07/27 Javascript
koa-router源码学习小结
2018/09/07 Javascript
基于jQuery实现可编辑的表格
2019/12/11 jQuery
JavaScript使用prototype属性实现继承操作示例
2020/05/22 Javascript
原生JS实现多条件筛选
2020/08/19 Javascript
Python中staticmethod和classmethod的作用与区别
2018/10/11 Python
python查看文件大小和文件夹内容的方法
2019/07/08 Python
Python3 全自动更新已安装的模块实现
2020/01/06 Python
解决python中import文件夹下面py文件报错问题
2020/06/01 Python
Python中的__init__作用是什么
2020/06/09 Python
Python Selenium破解滑块验证码最新版(GEETEST95%以上通过率)
2021/01/29 Python
5 个强大的HTML5 API 函数推荐
2014/11/19 HTML / CSS
台湾最大网路书店:博客来
2018/03/18 全球购物
人口与计划生育目标管理责任书
2014/07/29 职场文书
小学竞选班长演讲稿
2014/09/09 职场文书
项目技术负责人岗位职责
2015/04/13 职场文书
golang的文件创建及读写操作
2022/04/14 Golang
Android Gradle 插件自定义Plugin实现注意事项
2022/06/16 Java/Android