一般被问到如何实现深拷贝的时候,我都会回答JSON.parse(JSON.stringify()),然后介绍它的缺点,比如undefined和function会被忽略,日期类型会被转为字符串,正则会被转为空对象。遇到循环引用的话被直接报错。所以更加保险的方法是利用递归实现一个深拷贝。一段简简单单的前端八股文送给面试官。今天这个面试官来头不小,问我除了这两种方法还有其他方法实现吗?我当时心想:你是闰土吗,在这找猹吗?你这么问,迅哥儿知道吗?今天就来总结几种方法来实现深拷贝。
postMessage
平常我们在A页面使用iframe标签引入B页面时需要使用postMessage进行两个页面的通信,这里的对象也是进行的深拷贝,且不会像JSON.parse(JSON.stringify(objA))对有些熟悉无法处理,甚至报错。处理逻辑就是将A页面需要深拷贝的对象发送个B页面,B页面将这个对象原封不动的传回来即可。
A页面的代码
<div>A页面</div>
<button onclick="test()">测试</button>
<iframe src="./b.html" frameborder="0" id="bIframe"></iframe>
<script>
let objA = {
a: 1,
b: {
c: 2
},
c: null,
d: undefined,
f: /a/,
g: new Date(),
i: NaN
}
objA.j = objA
const bIframe = document.getElementById('bIframe')
window.onmessage = function(event){
let objB = event.data.objA
objB.b.c = 33333
console.log(objA)
console.log(objB)
}
function test() {
bIframe.contentWindow.postMessage({objA}, "*");
}
</script>
B页面代码
<div>B页面</div>
<script>
let objB = {a: 33}
window.onmessage = function(event){
objB = event.data
window.parent.postMessage(event.data, "*");
}
</script>
运行结果如下
可以看到objB的值修改没有影响objA同时特殊类型的值也能成功拷贝,循环引用也不会报错
Worker
原本web worker就是在web中使用的worker,这个worker是独立于web主线程的,在后台运行的线程。比如你的页面要加载一个业务比较耗时的js,你可以让worker单独开一个线程在后台处理,处理完了把结果返回给你,这样就不会阻塞主进程了。它也提供了一些通信方式,这些数据的传递也是深拷贝的。代码如下 A.html
<body>
<button onclick=" ()">测试</button>
<script>
let objA = {
a: 1,
b: {
c: 2
},
c: null,
d: undefined,
f: /a/,
g: new Date(),
i: NaN
}
objA.j = objA
let objB
function test() {
const work = new Worker('./worker.js');
work.postMessage({
objA
});
work.onmessage = function(event) {
objB = event.data.objA
objB.b.c = 333
console.log(objA);
console.log(objB);
}
}
</script>
</body>
worke.js
self.onmessage = function(event) {
self.postMessage(event.data);
}
indexDB
在HTML5的时候,新增了一种数据库名为indexDB,这种数据库是存证在客户端本地的NOSQL数据库,这种数据库也可以存储对象。利用它我们也可以实现深拷贝
<body>
<button id="add" onclick="add()">添加数据</button>
<button id="get" onclick="get()">获取数据</button>
<script>
let objA = {
a: 1,
b: {
c: 2
},
c: null,
d: undefined,
f: /a/,
g: new Date(),
i: NaN
}
objA.j = objA
let objB
var request = indexedDB.open("copyDB", 1)
request.onupgradeneeded =function(event){
const db = request.result;
const store = db.createObjectStore("Copy", {
keyPath: "id",
autoIncrement: true,
});
};
request.onsuccess = () => {
// 获取数据库连接
const db = request.result
// 创建事务对象
const tx = db.transaction("Copy", "readwrite")
// 创建一个与我们存储的事务
const copyStore = tx.objectStore("Copy")
}
function add() {
// 设置一个事务
const db = request.result
const tx = db.transaction("Copy", "readwrite")
const copyStore = tx.objectStore("Copy")
copyStore.put({objA}) // 添加到 indexedDB
}
function get() {
var request=indexedDB.open("copyDB");
request.onsuccess=function(){
var db=request.result;
var transaction=db.transaction("Copy","readonly");
var objStore=transaction.objectStore("Copy");
//从对象存储空间中读取数据
var result=objStore.get(1);
result.onsuccess=function(){
objB = result.result.objA
objB.b.c = 333
console.log(objA);
console.log(objB);
};
};
}
</script>
</body>
使用第三方库
最简单的和最稳妥的方法当然是使用第三方库去实现,比如函数库lodash的_.cloneDeep方法