小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
前言
前端程序员,可能没有谁没没复制对象吧? 今天就一天来学习学习对象的复制。
此对象,非彼对象。
为什么要进行复制
什么时候需要进项对象的复制??
- 不破坏原数据 原始数据可能还需要在别处使用,如果进行了更改,可能导致意外的结果。 比如函数的参数,构造函数的参数等等。
函数式编程中有一个重要的概念,副作用, 无副作用,其中有一条就是不要修改入参。
- 需要基于当前数据,创建新的数据,用于他用。
JSON.stringify + JSON.parse
如果全部是普通属性键,并且值类型没有function,symbol,Date,RegExp等等一些特殊对象, 或者类似 window这种循环引用的值。 其还是很有效的。
简单,暴力。
function clone(obj){
return JSON.parse(JSON.stringify(obj))
}
clone({a:1,{b:2}}) // {a:1,{b:2}}
// 时间:转为字符串
clone({date: new Date()}) // {date: '2021-09-26T08:23:40.517Z'}
// 正则: 变为了空对象 异常
clone({regex: /[0-9]/ }) // {regex: {…}}
// Blob: 变为空对象, 异常
clone({blob:new Blob(["123"])}) // {blob: {…}}
// 函数
clone({fn(){}}) // {}
// wubdiw
clone({window}) // Uncaught TypeError: Converting circular structure to JSON
更多的限制,可以查看JSON.stringify()
简单说,就是对纯数据类型比较有用,比如服务端返回的接口数据这种。
BroadcastChannel等等
浏览器很多内置的API,进行通讯的时候,会对传递的参数进行复制,我们可以直接利用这个功能。比如我们使用BroadcastChannel来进行复制。
var ch1 = new BroadcastChannel("_____[clone]_____");
var ch2 = new BroadcastChannel("_____[clone]_____");
function clone(data){
return new Promise((resolve)=>{
ch2.addEventListener("message", ev=>resolve(ev.data), {once: true});
ch1.postMessage(data);
})
}
// 复制对象: 正确
var obj1 = {a:{b:1}};
clone(obj1).then(a=>console.log(a, a === obj1)) // {a:{b:1}} false
// 复制时间: 正确, 依旧能调用getFullYear方法
var obj1 = {
a: new Date()
};
clone(obj1)
.then(a=>console.log(a, a === obj1, a.a.getFullYear()))
// {a: Sun Sep 26 2021 16:37:20 GMT+0800 (中国标准时间)} false 2021
// 复制正则, 正确,依旧能调用正则的test方法
var obj1 = {
a: /[0-9]/
};
clone(obj1)
.then(a=>console.log(a, a === obj1, a.a.test(1)))
// {a: /[0-9]/} false true
// 复制Blob, 正确,依旧还是Blob类型
var obj1 = {
a: new Blob(["123"])
};
clone(obj1)
.then(a=>console.log(a, a === obj1, a.a.test(1)))
// {a: Blob} false '[object Blob]'
// 复制window
var obj1 = {
a:1,
window
};
clone(obj1).then(a=>console.log(a, a === obj1))
// Uncaught (in promise) DOMException: Failed to execute 'postMessage' on 'BroadcastChannel': #<Window> could not be cloned
// 复制函数
var obj1 = {
a:1,
fn(){}
};
clone(obj1).then(a=>console.log(a, a === obj1))
// Uncaught (in promise) DOMException: Failed to execute 'postMessage' on 'BroadcastChannel': fn(){} could not be cloned.
当然缺点,就是把同步变成了异步。
function和window这中循环引用的依旧无法复制, 还会出异常,不过确实是一条路。- 其对引用类型的复制,保持了原有的方法属性,这是比
JSON.parse(JSON.stingify(obj))强大的地方。
其实, 其他消息通讯的API应该均是可以达到类似功能的,比如 window.postMessage, Storage的message事件,Web Worker等等。
小结
今天你收获了吗?