前端的深拷贝和浅拷贝_前端面试-深拷贝和浅拷贝
面试题目:如何实现对一个数组或对象的浅拷贝和深拷贝?
WTF,复制还分两种,第一次遇到这种问题的时候很是无语呢,先来看看一般的答案的理解。
浅拷贝是只拷贝一层,深层次的对象级别就只拷贝引用。 深拷贝是拷贝多层,每一级别的数据都拷贝出来。也就是说,基本数据类型其实不存在深浅拷贝的问题,只有对象和数组才存在深浅拷贝的问题。
主要解决的是什么问题呢?你去买房子,看中一套不错要了,然后中介给你打印了一份合同,你签字付钱。过一段时间去看,哎呀我去,怎么装修了?另外一个人也拿着同样有合同、付款凭证。我以为是我买的房子,结果中介一房两卖,别人也能搞。这怎么行?
JS数据类型
js分为基本数据类型和复杂数据类型。
基本数据类型包括:String、Number、Boolean、Null、Undefined
复杂(引用)类型包括:Object、Array、Function
【前端的深拷贝和浅拷贝_前端面试-深拷贝和浅拷贝】在开发过程中,经常使用 typeof 来检测数据类型。默认var声明的时候,如果不进行赋值,类型就是undefined。布尔值是boolean只有 true,false两种值。
声明的时候用的null,这时候代表空对象,使用typeof检测的时候,显示是Object。
JS内存管理
JS代码运行的时候,数据都要写入内存进行调用的,而不同的数据类型在内存中存放的方式是不一样的。
基本数据类型是存储在栈数据空间中,复杂数据类型是存储在堆数据空间中的,而对数据空间不能直接访问,需要栈这边进行位置指引。
一个不是很恰当的比喻就是内存相当于仓库。
仓库里面分了两个区域,一边是都是小格子,另一边都是大货柜。简单数据类型比如你的一本书,你的一份账单什么的就直接放在小格子里面就好了。
另外你有一屋子书和一屋子的账单,小格子放不下。你就租了一个小格子和一个仓库。小格子里面放着仓库的钥匙和仓库的位置,仓库里面放东西。
实际的内存读取方式也类似。要找自己的小格子,你就要从上到下挨着找。想要找自己货柜里面的东西,还是需要先去小格子里面找到存放钥匙和位置的格子,找到以后直接去找货柜。
下图是
文章图片
浅拷贝和深拷贝
再没有了解到深拷贝和浅拷贝知识的时候,一般拷贝就是从新赋值。声明个数据直接用另外一个对象赋值。这个就算是浅拷贝。
当遇到复杂对象的时候,复制的只是对象的指针,并没有重新开辟大的空间进行复制。这时候造成的影响就是对两个指针进行数据操作的时候,操作的是同一个数据内容,相互之间是受影响的。
而深拷贝就是需要连指针到内容都进行复制,两个指针指向两个空间的内容。各自操作已经不受影响。
浅拷贝的实现方式
浅拷贝的复制就是直接复制赋值就可以了。
方法一:
function simpleClone(initalObj) { var obj = {};
for ( var i in initalObj) { obj[i] = initalObj[i];
} return obj;
}var obj = { b:{ a: "world", b: 21 }, c:["Bob", "Tom", "Jenny"], d:function() { alert("hello world");
}}var cloneObj = simpleClone(obj);
console.log(cloneObj.b);
console.log(cloneObj.c);
console.log(cloneObj.d);
cloneObj.b.a = "changed";
cloneObj.c = [1, 2, 3];
cloneObj.d = function() { alert("changed");
};
console.log(obj.b);
console.log(obj.c);
console.log(obj.d);
自行运行查看下变化及原因。
方法二: Object.assign是ES6的新函数。Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是 Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。
Object.assign(target, ...sources)var obj = { a: {a: "hello", b: 21} };
var initalObj = Object.assign({}, obj);
initalObj.a.a = "changed";
console.log(obj.a.a);
// "changed"需要注意的是:
Object.assign()可以处理一层的深度拷贝,如下:
var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = Object.assign({}, obj1);
obj2.b = 100;
console.log(obj1);
// { a: 10, b: 20, c: 30 }
如果要复制的对象只有一层,对象里面的元素全是基本元素的话,前面的浅拷贝案例其实就完成了深拷贝的功能。我们重点说一下多层对象。
1.通过JSON转换 用JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象。
但是这种方法也有不少坏处,譬如它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。并且只能处理常见的Number, String, Boolean, Array, 扁平对象等这些能被JSON表示的数据结构。RegExp对象是无法通过这种方式深拷贝。
2.递归拷贝
function deepClone(initalObj, finalObj) { var obj = finalObj || {};
for (var i in initalObj) { var prop = initalObj[i];
if(prop === obj) { continue;
} if (typeof prop === 'object') { obj[i] = (prop.constructor === Array) ? [] : {};
arguments.callee(prop, obj[i]);
} else { obj[i] = prop;
} } return obj;
}递归拷贝就是将对象逐层解开进行剖析,逐层新建对象,逐层复制,知道最深处的所有简单数据都复制上。
但是要注意要注意对象引用对象的情况,会掉入死循环。
3.使用Object.create()方法 直接使用var newObj = Object.create(oldObj),可以达到深拷贝的效果。
function deepClone(initalObj, finalObj) { var obj = finalObj || {};
for (var i in initalObj) { var prop = initalObj[i];
// 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况 if(prop === obj) { continue;
} if (typeof prop === 'object') { obj[i] = (prop.constructor === Array) ? [] : Object.create(prop);
} else { obj[i] = prop;
} } return obj;
}小案例
之前在进行公司VUE项目开发的过程中,由于需要将富文本编辑器抽离成为一个单独的组件,然后将内容的对象传入进去。如果按照传统的vue组件开发的流程,肯定是要接收传入、赋值给本组件、本组件编辑器修改、修改完毕的内容再进行emit外传,然后组件外部接受,进一步处理。
但是由于vue组件之间浅拷贝的特性,其实传入的对象修改之后,外部组件直接取值拿到的就是最新的值。
也是因为这个发现才对深拷贝和浅拷贝有了更加深入的了解。
推荐阅读
- 面试|前端面试——深拷贝与浅拷贝的区别
- 前端面试题|深拷贝和浅拷贝
- java小贴士|面试官问(什么是浅拷贝和深拷贝())
- 面试|面试题-深拷贝和浅拷贝区别是什么()
- 一些零碎代码|适用于eclipse的codetemplate.xml
- Javascript|前端js面试会问到的浅拷贝和深拷贝(以浅拷贝为例)
- 读Flink源码谈设计(FileSystemConnector中的整洁架构)
- 3个案例,详解如何选择合适的研发模式
- 【北亚数据恢复】重装系统后磁盘分区丢失的XFS文件系统服务器数据恢复案例
- 微服务学习|Nginx学习笔记