我的编程空间,编程开发者的网络收藏夹
学习永远不晚

js深拷贝和浅拷贝的深入讲解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

js深拷贝和浅拷贝的深入讲解

浅拷贝

创建一个新的对象,来接受你要重新复制或引用的对象值。如果对象属性是基本的数据类型,复制的就是基本类型的值给新对象;但如果属性是引用数据类型,复制的就是内存中的地址,如果其中一个对象改变了这个内存中的地址,会影响到另一个对象。

实现方法

方法一:Object.assign

es6 中 object 的一个方法,用于 JS 对象的合并等,返回目标对象。它不会拷贝对象的继承属性和不可枚举的属性

let target = {};
let source = {a:{b:1}};
Object.assign(target,source)
console.log(taget) // {a:{b:1}}
target.a.b = 2;
console.log(taget) // {a:{b:2}}
console.log(source) // {a:{b:2}}

方法二:扩展运算符方式


let obj = {a:1,b:{c:1}}
let obj2 = {...obj}
obj.a = 2
console.log(obj)  //{a:2,b:{c:1}}
console.log(obj2); //{a:1,b:{c:1}}
obj.b.c = 2
console.log(obj)  //{a:2,b:{c:2}} 
console.log(obj2); //{a:1,b:{c:2}}

let arr = [1, 2, 3];
let newArr = [...arr]; 

方法三:concat和slice 浅拷贝数组

仅仅针对数组类型,都会返回一个新的数组对象。

concat 浅拷贝数组
let arr = [1, 2, 3];
let newArr = arr.concat();
newArr[1] = 100;
console.log(arr);  // [ 1, 2, 3 ]
console.log(newArr); // [ 1, 100, 3 ]

slice 浅拷贝数组
let arr = [1, 2, {val: 4}];
let newArr = arr.slice();
newArr[2].val = 1000;
console.log(arr);  //[ 1, 2, { val: 1000 } ]

深拷贝

将一个对象从内存中完整地拷贝出来一份给目标对象,并从堆内存中开辟一个全新的空间存放新对象,且新对象的修改并不会改变原对象,二者实现真正的分离。

实现方法

方法一:乞丐版(JSON.stringify和JSON.parse)

let obj1 = { a:1, b:[1,2,3] }
let str = JSON.stringify(obj1);
let obj2 = JSON.parse(str);
console.log(obj2);   //{a:1,b:[1,2,3]} 
obj1.a = 2;
obj1.b.push(4);
console.log(obj1);   //{a:2,b:[1,2,3,4]}
console.log(obj2);   //{a:1,b:[1,2,3]}

缺陷:

  • 拷贝的对象的值中如果有函数、undefined、symbol 这几种类型,经过 JSON.stringify 序列化之后的字符串中这个键值对会消失
  • 拷贝 Date 引用类型会变成字符串
  • 无法拷贝不可枚举的属性
  • 无法拷贝对象的原型链
  • 拷贝 RegExp 引用类型会变成空对象
  • 对象中含有 NaN、Infinity 以及 -Infinity,JSON 序列化的结果会变成 null

手写递归实现

基础版

 let obj = {a:{b:1}};
 function deepClone(obj){
  let cloneObj = Object.prototype.toString.call(obj) === "[object Array]" ? [] : {};
   for(let key in obj){
     if(typeof obj[key]=== "object" && obj !== null){
       cloneObj[key] = deepClone(obj[key])
     }else{
       cloneObj[key] = obj[key]
     }
   }
   return cloneObj;
 }
 let obj2 = deepClone(obj);
 obj2.a.b = 33
 console.log("obj",obj) // {a:{b:1}}
 console.log("obj2",obj2) // {a:{b:33}}

缺陷:  

  • 不能复制不可枚举的属性以及 Symbol 类型  
  • 这种方法只是针对普通的引用类型的值做递归复制,而对于 Array、Date、RegExp、Error、Function 这样的引用类型并不能正确地拷贝
  • 对象的属性里面成环,即循环引用没有解决

改进版

const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null)
const deepClone = function (obj, hash = new WeakMap()) {
  if (obj.constructor === Date) 
  return new Date(obj)       // 日期对象直接返回一个新的日期对象
  if (obj.constructor === RegExp)
  return new RegExp(obj)     //正则对象直接返回一个新的正则对象
  //如果循环引用了就用 weakMap 来解决
  if (hash.has(obj)) return hash.get(obj)
  let allDesc = Object.getOwnPropertyDescriptors(obj)
  //遍历传入参数所有键的特性
  let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc)
  //继承原型链
  hash.set(obj, cloneObj)
  for (let key of Reflect.ownKeys(obj)) { 
    cloneObj[key] = (isComplexDataType(obj[key]) && typeof obj[key] !== 'function') ? deepClone(obj[key], hash) : obj[key]
  }
  return cloneObj
}

手写深拷贝参考:

https://www.jb51.net/article/247540.htm

https://www.jb51.net/article/247544.htm

其他实现方法

lodash库的_.cloneDeep方法,jQuery.extend()方法

FAQ:赋值和深浅拷贝的区别

注意:前提都是针对引用类型

赋值

当我们把一个对象赋值给一个新的变量时,赋的其实是该对象的在栈中的地址,而不是堆中的数据。也就是两个对象指向的是同一个存储空间,无论哪个对象发生改变,其实都是改变的存储空间的内容,因此,两个对象是联动的。

let a = {name:'jimmy',b:{age:12}}
let b = a;
b.name = 'chimmy';
b.b.age = 21;
console.log(a) //  {name:'chimmy',b:{age:21}}
console.log(b) //  {name:'chimmy',b:{age:21}}

浅拷贝

重新在堆中创建内存,拷贝前后对象的基本数据类型互不影响,但拷贝前后对象的引用类型因共享同一块内存,会相互影响。

let a = {name:'jimmy',b:{age:12}}
let b = {...a};
b.name = 'chimmy';
b.b.age = 21;
console.log(a) //  {name:'jimmy',b:{age:21}}
console.log(b) //  {name:'chimmy',b:{age:21}}

深拷贝

将一个对象从内存中完整地拷贝出来一份给目标对象,并从堆内存中开辟一个全新的空间存放新对象,且新对象的修改并不会改变原对象,二者实现真正的分离。

let a = {name:'jimmy',b:{age:12}}
let b = {...a};
b.name = 'chimmy';
b.b.age = 21;
console.log(a) //  {name:'jimmy',b:{age:12}}
console.log(b) //  {name:'chimmy',b:{age:21}}

总结

到此这篇关于js深拷贝和浅拷贝的文章就介绍到这了,更多相关js深拷贝和浅拷贝内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

js深拷贝和浅拷贝的深入讲解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

深入理解python中的浅拷贝和深拷贝

在讲什么是深浅拷贝之前,我们先来看这样一个现象:a = ['scolia', 123, [], ] b = a[:] b[2].append(666) print a print b为什么我只对b进行修改,却影响到了a呢?看过我在之前的文章
2022-06-04

浅拷贝&深拷贝

浅拷贝新的对象指向原来对象的地址深拷贝新的对象中,原来是可变对象,会新复制一份值指向新的地址[11,22,33]若原来的对象里含有可变对象,里面的这个可变对象也会指向新的地址['qwer', 123, [44,55]]参考:https://
2023-01-30

python浅拷贝和深拷贝

python中的赋值是按引用来传递的,如果不是赋值而是拷贝,那就需要用到copy模块了,这就不得不谈浅拷贝和深拷贝了。 浅拷贝copy() #!/usr/bin/python  import copy  class MyClass:    
2023-01-31

JS怎么实现深拷贝和浅拷贝

这篇文章主要介绍“JS怎么实现深拷贝和浅拷贝”,在日常操作中,相信很多人在JS怎么实现深拷贝和浅拷贝问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”JS怎么实现深拷贝和浅拷贝”的疑惑有所帮助!接下来,请跟着小编
2023-06-30

浅拷贝与深拷贝

名词解释1.对象:被分配的一块内存,存储其所代表的值2.引用:是自动形成的从变量到对象的指针3.注意:类型(int类型,long类型(python3已去除long类型,只剩下int类型的数据))属于对象,不是变量4.不可变对象:一旦创建就不
2023-01-30

python深拷贝浅拷贝

python深拷贝和浅拷贝问题:  什么是深拷贝?    (个人理解)深拷贝就是将原有的数据一模一样的拷贝一份,然后存到另一个地址中,而不是引用地址  什么是浅拷贝?    (个人理解)就是引用地址(1)用等于号的拷贝都属于浅拷贝     
2023-01-30

分析JavaScript浅拷贝和深拷贝

本篇内容主要讲解“分析JavaScript浅拷贝和深拷贝”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“分析JavaScript浅拷贝和深拷贝”吧!一、直接赋值对象是引用类型,如果直接赋值给另外一
2023-06-25

python深拷贝与浅拷贝

可变对象与不可变对象要理解深拷贝和浅拷贝,首先要理解可变对象和不可变对象。不可变对象:该对象所指向的内存中的值不能被改变,修改对象的值时,由于其指向的值不能被改变,因此实际上是在内存中重新开辟一个地址用来存储新的值,然后将对象指向这个新值。
2023-01-30

编程热搜

目录