赞
踩
在js中我们想要实现深拷贝,首先要了解深浅拷贝的区别。
浅拷贝:只是拷贝数据的内存地址,而不是在内存中重新创建一个一模一样的对象(数组)
深拷贝:在内存中开辟一个新的存储空间,完完全全的拷贝一整个一模一样的对象(数组)
MDN资料:https://developer.mozilla.org/zh-CN/docs/Web/API/MessageChannel
MessageChannel
的介绍Channel Messaging
API
的 MessageChannel
接口允许我们创建一个新的消息通道,并通过它的两个 MessagePort
属性发送数据。
备注: 此特性在 Web Worker 中可用
封装一个deepclone函数
function deppClone(obj) {
return new Promise(resolve => {
const { port1, port2 } = new MessageChannel()
port1.postMessage(obj)
port2.onmessage = e => {
resolve(e.data)
}
})
}
function deppClone(obj) { return new Promise(resolve => { const { port1, port2 } = new MessageChannel() port1.postMessage(obj) port2.onmessage = e => { resolve(e.data) } }) } const obj = { a: "", c: undefined, e: 0, f: [], g: NaN, h: null } obj.b = obj let newObj = null await deppClone(obj).then(res => { newObj = res }) console.log("obj", obj) console.log("newObj", newObj) console.log(obj === newObj) console.log(obj.b === newObj) console.log(obj === newObj.b)
他可以完美解决循环引用的问题。
支持的浏览器版本比较多 (文献资料)
不支持拷贝函数
不支持拷贝Symbol
文献资料:https://developer.mozilla.org/zh-CN/docs/web/api/structuredClone
structuredClone()
的介绍全局的 structuredClone()
方法使用结构化克隆算法将给定的值进行深拷贝。
该方法还支持把原始值中的可转移对象转移到新对象,而不是把属性引用拷贝过去。 可转移对象与原始对象分离并附加到新对象;它们不可以在原始对象中访问被访问到。
structuredClone(obj)
const obj = { a: "",c:undefined,e:0,f:[],g:NaN,h:null }
obj.b = obj
const newObj = structuredClone(obj)
console.log('obj',obj)
console.log('newObj',newObj)
console.log(obj === newObj)
console.log(obj.b === newObj)
console.log(obj === newObj.b)
他可以完美解决循环引用的问题。
而且非常简单,调用API即可。
这是一个新的API,它支持的浏览器版本比较新
不支持拷贝函数
不支持拷贝Symbol
最经典的就是手动封装一个deepClone函数,去主动判断传入类型并且递归创建新的对象
封装函数,并判断类型
function isObject(value) { const valueType = typeof value return (value !== null) && (valueType === "object" || valueType === "function") } function deepClone(originValue, map = new WeakMap()) { // 判断是否是一个Set类型 if (originValue instanceof Set) { return new Set([...originValue]) } // 判断是否是一个Map类型 if (originValue instanceof Map) { return new Map([...originValue]) } // 判断如果是Symbol的value, 那么创建一个新的Symbol if (typeof originValue === "symbol") { return Symbol(originValue.description) } // 判断如果是函数类型, 那么直接使用同一个函数 if (typeof originValue === "function") { return originValue } // 判断传入的originValue是否是一个对象类型 if (!isObject(originValue)) { return originValue } if (map.has(originValue)) { return map.get(originValue) } // 判断传入的对象是数组, 还是对象 const newObject = Array.isArray(originValue) ? []: {} map.set(originValue, newObject) for (const key in originValue) { newObject[key] = deepClone(originValue[key], map) } // 对Symbol的key进行特殊的处理 const symbolKeys = Object.getOwnPropertySymbols(originValue) for (const sKey of symbolKeys) { // const newSKey = Symbol(sKey.description) newObject[sKey] = deepClone(originValue[sKey], map) } return newObject }
let s1 = Symbol("aaa") let s2 = Symbol("bbb") const obj = { a: 18, b: { c: "www", d: { e: "www" } }, // 数组类型 hobbies: ["abc", "cba", "nba"], // 函数类型 foo: function(m, n) { console.log("wwww") console.log("wwww") return 123 }, // Symbol作为key和value [s1]: "abc", s2: s2, // Set/Map set: new Set(["aaa", "bbb", "ccc"]), map: new Map([["aaa", "abc"], ["bbb", "cba"]]) } obj.info = obj const newObj = deepClone(obj) console.log(newObj === obj) console.log('obj',obj) console.log('newObj',newObj) console.log(newObj.s2 === obj.s2)
他可以完美解决各种问题。
这是最不推荐使用的方法
优点:这是最简单的方式了,只能处理不复杂的对象
const symbol1 = Symbol();
const obj = { a: "",c:undefined,e:0,f:[],g:NaN,h:null,i:symbol1 }
let newObj = JSON.parse(JSON.stringify(obj))
console.log('obj',obj)
console.log('newObj',newObj)
缺点:
不能解决循环引用
NaN问题
忽略Symbol(),undefined
比如lodash、jQuery等插件的实现方式就不多讲了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。