详解js中的对象的深浅拷贝

贾宝玉
• 阅读 2490

前言

本文是整理的浅拷贝和深拷贝中涉及的知识点,在工作中是非常重要的,在面试中也是必考的,希望对小伙伴们有所帮助!
详解js中的对象的深浅拷贝

为什么会产生深浅拷贝?

首先我们要知道一个流程

1,对象属于引用类型的,以后浏览器会为其开辟一个新的内存空间,并为它分配一个16进制的地址

2,按照一定的顺序,把对象的键值对存储到内存空间

3,把开辟的内存地址赋值给变量(或事件),以后变量就通过地址找到内存空间,然后进行操作

基本数据类型和引用数据类型

数据分为基本数据类型和引用数据类型

基本数据类型

String、Number、Boolean、Null、Undefined、Symbol

基本数据类型是直接存储在栈中的数据

let str1 = '123';
str2 = str1;
str2 = '456';
console.log(str1); // '123'
console.log(str2); // '456'

形象举例:

我之前买了一双鞋子,我现在又买了一双,某一双坏了不会影响到另外一双

引用数据类型

Array、Object

引用数据类型存储的是该对象在栈中引用,真实的数据存储在内存中

let arr1 =[1,2,3,4,5,6];
arr2 = arr1;
arr2.pop();
console.log(arr1);//[ 1, 2, 3, 4, 5 ]
console.log(arr2);//[ 1, 2, 3, 4, 5 ]

形象举例:

在大草原上,有一些羊吃了腐烂的草得病死了。草原还是草原,但是内部的草变少了;羊群还是羊群,但是内部的羊变少了

深拷贝和浅拷贝的概念

浅拷贝:

仅仅复制对象的引用,而不是对象本身

深拷贝:

把复制的对象所引用的全部对象都复制一遍

深拷贝和浅拷贝的区别

详解js中的对象的深浅拷贝
详解js中的对象的深浅拷贝

~ 和原数据是否指向同一对象 第一层数据为基本数据类型 原数据中包含子对象
赋值 改变会使数据一同改变 改变会使原数据一同改变
浅拷贝 改变不会使原数据一同改变 改变会使数据一同改变
深拷贝 改变不会使原数据一同改变 改变不会使原数据一同改变

浅拷贝

通用循环

const arr1 = [1, 2, ['ming', 'abc'], 5];

const shallowClone = (arr) => {
  const dst = [];
  for (let prop in arr) {
    if (arr.hasOwnProperty(prop)) {
        dst[prop] = arr[prop];
    }
  }
  return dst;
}

const arr2 = shallowClone(arr1);
arr2[2].push('wuhan');
arr2[3] = 5;

console.log(arr1);
console.log(arr2);

运行结果:
详解js中的对象的深浅拷贝

object.assign()

  • Object.assign()方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象
  • Object.assign()进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身
    const obj1 = {
        username:'ming',
        skill:{
            play:['backetball','game'],
            rend: 'book',
        },
        girlfriends:['xiaomi','xiaohong','xiaolan'],
    };
    const obj2 = Object.assign({},obj1);
    obj2.username = 'memg';//修改基本类型
    obj2.skill.read = 'e-mail';//修改二层基本类型
    obj2.skill.play = ['footbool'];//修改二层引用类型
    obj2.girlfriend = ['xiaoming'];
    console.log(obj1);
    console.log(obj2);

运行结果:
详解js中的对象的深浅拷贝

Array.prototype.concat()

concat()是数组的一个内置方法,用户合并两个或者多个数组

这个方法不会改变现有数组,而是返回一个新数组

  const arr1 = [1,{username: 'ming',},];
  let arr2 = arr1.concat();
  arr1[0] = 2;
  arr1[1].username = 'meng';
  console.log(arr1);
  console.log(arr2);

运行结果:
详解js中的对象的深浅拷贝

Array.prototype.slice()

slice()也是数组的一个内置方法,该方法会返回一个新的对象

slice()不会改变原数组

const arr1 = [1,{username:'ming',},];
let arr2 = arr1.slice();
arr2[0] = 2;
arr2[1].username = 'meng'
console.log(arr1);
console.log(arr2); 

运行结果:
详解js中的对象的深浅拷贝

obj展开运算符

展开运算符是ES6中新提出来的一种运算符

在拷贝数组、对象以及拼接数组等方面都可以使用

这步我们也可以尝试下使用const obj2 = {...obj1}的形式进行浅拷贝

//拷贝数组
const arr1 = [1, 2, 3];
const arr2 = [...arr1]; // like arr.slice()
arr2.push(8); 

console.log(arr1); // [ 1, 2, 3 ]
console.log(arr2); // [ 1, 2, 3, 8 ]

//拷贝对象
const obj1 = {
  name: 'ming',
  arr1: ['9', '7', '6'],
  obj: {
    name: 'meng',
    arr2: ['7', '8', '9'],
  },
};
const obj2 = {...obj1};// like arr.slice()
obj2.name = 'ming2';
obj2.arr1 = ['null'];
obj2.obj.name = 'meng2';
obj2.obj.arr2 = ['null'];

console.log(obj1);
console.log(obj2);

运行结果:
详解js中的对象的深浅拷贝

深拷贝

手动递归

function deepClone (sourceObj, targetObj) {
    let cloneObj = targetObj || {}
    if(!sourceObj || typeof sourceObj !== "object" || sourceObj.length === undefined){
        return sourceObj
    }
    if(sourceObj instanceof Array){
        cloneObj = sourceObj.concat()
    } else {
        for(let i in sourceObj){
            if (typeof sourceObj[i] === 'object') {
                cloneObj[i] = deepClone(sourceObj[i], {})
            } else {
                cloneObj[i] = sourceObj[i]
            }
        }
    }
    return cloneObj
}
let sourceObj = {
  a: 1,
  b: {
    a: 1
  },
  c: {
    a: 1,
    b: {
      a: 1
    }
  },
  d: function() {
    console.log('hello world')
  },
  e: [1, 2, 3]
}
let targetObj = deepClone(sourceObj, {})
targetObj.c.b.a = 9
console.log(sourceObj)
console.log(targetObj)

运行结果:
详解js中的对象的深浅拷贝

Json.parse(Json.Stringify())

JSON.stringify():把一个js对象序列化为一个JSON字符串

JSON.parse():把JSON字符串反序列化为一个js对象

const arr9 = [
    1,
    {
      username:'ming',
    },
  ];
  let arr10 = JSON.parse(JSON.stringify(arr9));
  arr10[0]=2;
  arr10[1].username='meng';
  console.log(arr9);
  console.log(arr10);

运行结果:
详解js中的对象的深浅拷贝

let obj = {
    name: 'ming',
    age: 20,
    friend: {
      name: 'ming1',
      age: 19
    }
  };
  let copyObj = JSON.parse(JSON.stringify(obj));
  obj.name = 'meng';
  obj.friend.name = 'meng1';
  console.log(obj);
  console.log(copyObj);

运行结果:
详解js中的对象的深浅拷贝

函数库Lodash

Lodash作为JavaScript函数库/工具库,它里面有非常好用的封装好的功能

var _= require('lodash');
const obj1 = [
  1,
  'Hello!',
  { name:'ming1' },
  [
    {
      name:'meng1',
    }
  ],
]
const obj2 = _.cloneDeep(obj1);
obj2[0] = 2;
obj2[1] = 'Hi!';
obj2[2].name = 'ming2'
obj2[3][0].name = 'meng2';
console.log(obj1);
console.log(obj2);

运行结果:
详解js中的对象的深浅拷贝

最后

如果本文对你有帮助得话,给本文点个赞❤️❤️❤️

欢迎大家加入,一起学习前端,共同进步!
详解js中的对象的深浅拷贝
详解js中的对象的深浅拷贝

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
java 复制Map对象(深拷贝与浅拷贝)
java复制Map对象(深拷贝与浅拷贝)CreationTime2018年6月4日10点00分Author:Marydon1.深拷贝与浅拷贝  浅拷贝:只复制对象的引用,两个引用仍然指向同一个对象
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Wesley13 Wesley13
3年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
美凌格栋栋酱 美凌格栋栋酱
5个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(