摘要:那么如何切断和之间的关系呢,可以拷贝一份的数据,根据拷贝的层级不同可以分为浅拷贝和深拷贝,浅拷贝就是只进行一层拷贝,深拷贝就是无限层级拷贝。
深拷贝 vs 浅拷贝
深拷贝和浅拷贝都是针对的引用类型,JS中的变量类型分为值类型(基本类型)和引用类型;对值类型进行复制操作会对值进行一份拷贝,而对引用类型赋值,则会进行地址的拷贝,最终两个变量指向同一份数据。
// 基本类型 var a = 1; var b = a; a = 2; console.log(a, b); // 2, 1 ,a b指向不同的数据 // 引用类型指向同一份数据 var a = {c: 1}; var b = a; a.c = 2; console.log(a.c, b.c); // 2, 2 全是2,a b指向同一份数据
对于引用类型,会导致a b指向同一份数据,此时如果对其中一个进行修改,就会影响到另外一个,有时候这可能不是我们想要的结果,如果对这种现象不清楚的话,还可能造成不必要的bug。那么如何切断a和b之间的关系呢,可以拷贝一份a的数据,根据拷贝的层级不同可以分为浅拷贝和深拷贝,浅拷贝就是只进行一层拷贝,深拷贝就是无限层级拷贝。
深复制和浅复制最根本的区别在于是否是真正获取了一个对象的复制实体,而不是引用
深复制在计算机中开辟了一块内存地址用于存放复制的对象,
浅复制仅仅是指向被复制的内存地址,如果原地址中对象被改变了,那么浅复制出来的对象也会相应改变。
var a1 = {b: {c: {}}; var a2 = shallowClone(a1); // 浅拷贝 a2.b.c === a1.b.c // true var a3 = clone(a1); // 深拷贝 a3.b.c === a1.b.c // false
所谓的浅复制,只是拷贝了基本类型的数据,而引用类型数据,复制后也是会发生引用,我们把这种拷贝叫做“(浅复制)浅拷贝”。Object.assign({}, obj1, obj2)
function shallowClone(source) { var target = {}; for(var i in source) { if (source.hasOwnProperty(i)) { target[i] = source[i]; } } return target; }
深拷贝的问题其实可以分解成两个问题,浅拷贝+递归,什么意思呢?假设我们有如下数据
var a1 = {b: {c: {d: 1}};
只需稍加改动上面浅拷贝的代码即可,注意区别
function clone(source) { var target = {}; for(var i in source) { if (source.hasOwnProperty(i)) { if (typeof source[i] === "object") { target[i] = clone(source[i]); // 注意这里 } else { target[i] = source[i]; } } } return target; }
其实上面的代码问题太多了,先来举几个例子吧
没有对参数做检验
function isObject(x) { return Object.prototype.toString.call(x) === "[object Object]"; } function clone(source) { if (!isObject(source)) return source; // xxx }
判断是否对象的逻辑不够严谨
没有考虑数组的兼容
递归方法最大的问题在于爆栈,当数据的层次很深是就会栈溢出
方法: 用系统自带的JSON来做深拷贝的例子,下面来看下代码实现
function cloneJSON(source) { return JSON.parse(JSON.stringify(source)); }
但是cloneJSON也有缺点,它的内部也是使用递归的方式
cloneJSON(createData(10000)); // Maximum call stack size exceeded
既然是用了递归,那循环引用呢?并没有因为死循环而导致栈溢出啊,原来是JSON.stringify内部做了循环引用的检测,正是我们上面提到破解循环引用的第一种方法:循环检测
var a = {}; a.a = a; cloneJSON(a) // Uncaught TypeError: Converting circular structure to JSON
如何复制对象而不发生引用呢,对于数组,ES6我们复制有新的两种方法,不会发生引用。
第一种:Array.from(要复制的数组);
var arr1=[1,2,3]; var arr2=Array.from(arr1); arr1.push(4); alert(arr1); //1234 alert(arr2); //123 arr2.push(5); alert(arr1); //1234 alert(arr2); //1235
第二种:...
var arr1=[1,2,3]; var arr2=[...arr1]; arr1.push(4); alert(arr1); //1234 alert(arr2); //123 arr2.push(5); alert(arr1); //1234 alert(arr2); //1235
第二种这个方法也可以用在函数的行参上面。
function show(...arr1){ //直接来复制arguments这个伪数组,让它变成真正的数组,从而拥有数组的方法。 alert(arr1); //1234 arr1.push(5); alert(arr1); //12345 } show(1,2,3,4)
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/100128.html
摘要:深拷贝浅拷贝本文主要对深拷贝浅拷贝的解释及实现做一下简单记录。之所以会有深拷贝与浅拷贝之分,是因为不同数据类型的数据在内存中的存储区域不一样。但注意,只能做一层属性的浅拷贝。 深拷贝VS浅拷贝 本文主要对深拷贝&浅拷贝的解释及实现做一下简单记录。原文链接,欢迎star。 之所以会有深拷贝与浅拷贝之分,是因为不同数据类型的数据在内存中的存储区域不一样。 堆和栈是计算机中划分出来用来存储的...
摘要:而在这个运算符的相关用例中,往往会涉及到其他知识点,深拷贝和浅拷贝就是其中之一。即对象的浅拷贝会对主对象的值进行拷贝,而该值有可能是一个指针,指向内存中的同一个对象。,可以看到深拷贝和浅拷贝是对复制引用类型变量而言的。 在ES6的系列文章中,基本都会提到Spread——扩展运算符(...)。而在这个运算符的相关用例中,往往会涉及到其他知识点,深拷贝和浅拷贝就是其中之一。 背景知识 在讨...
阅读 2870·2021-11-25 09:43
阅读 2281·2021-11-24 09:39
阅读 2610·2021-09-23 11:51
阅读 1357·2021-09-07 10:11
阅读 1416·2019-08-27 10:52
阅读 1906·2019-08-26 12:13
阅读 3307·2019-08-26 11:57
阅读 1342·2019-08-26 11:31