目录

1. 基础版本v-1
2. 增加数组类型v-2
3. 增加函数类型v-3
4. 增加Set、Map、Date类型v-4
5. 增加Symbol类型v-5
6. 增加循环引用(最终版)
7. 另一种方法:MessageChannel + Promise

1. 基础版本v-1

javascript
// 深拷贝函数,参数是一个待拷贝的对象 function deepClone(originValue) { // 如果传入的是null或者不是对象类型, 那么直接返回 if(originValue == null || typeof originValue !== 'object') { return originValue } // 创建一个新对象,递归拷贝属性 const newObj = {} for(const key in originValue) { // 不拷贝原型上的 if(originValue.hasOwnProterty(key)) { newObj[key] = deepClone(originValue[key]) } } return newObj }

测试代码

javascript
const obj = { name: 'zhangsan', address: { city: 'hangzhou' } } const newObj = deepClone(obj) obj.address.city = 'beijing' console.log(newObj) // { name: 'zhangsan', address: { city: 'hangzhou' } }

可以看到 obj.address.city 改成了 beijing 但是 newObj.address.city 还是 hangzhou。说明已经对属性是对象类型作了深拷贝

2. 增加数组类型v-2

javascript
function deepClone(originValue) { if(originValue == null || typeof originValue !== 'object') { return originValue } // 判断传入的是数组还是对象 const newObj = Array.isArray(originValue) ? [] : {} for(const key in originValue) { // 不拷贝原型上的 if(originValue.hasOwnProterty(key)) { newObj[key] = deepClone(originValue[key]) } } return newObj }

测试代码

javascript
const obj = { name: 'zhangsan', address: { city: 'hangzhou' }, friends: ['zhangsan', 'lisi'] } const newObj = deepClone(obj) obj.address.city = 'beijing' obj.friends[0] = 'wangwu' console.log(newObj) // { // name: 'zhangsan', // address: { city: 'hangzhou' }, // friends: [ 'zhangsan', 'lisi' ] //}

可以看到 obj.friends[0] 改成了 wangwu 但是 newObj.friends[0] 还是 zhangsan。说明已经对属性是数组类型作了深拷贝

3. 增加函数类型v-3

函数一般认为只使用来进行代码的复用,不需要进行深拷贝,但若实现一下会更好。

js
function deepClone(originValue) { // 判断是否是函数类型 if (originValue instanceof Function) { // 将函数转成字符串 let str = originValue.toString() // 截取函数体内容字符串 let subStr = str.substring(str.indexOf("{") + 1, str.lastIndexOf("}")) // 利用截取函数体内容的字符串和函数的构造器重新生成函数并返回 return new Function(subStr) } if(originValue == null || typeof originValue !== 'object') { return originValue } // 判断传入的是数组还是对象 const newObj = Array.isArray(originValue) ? [] : {} for(const key in originValue) { // 不拷贝原型上的 if(originValue.hasOwnProterty(key)) { newObj[key] = deepClone(originValue[key]) } } return newObj }

测试

javascript
const obj = { foo: function () { console.log("function"); }, }; const newObj = deepClone(obj); console.log(obj.foo === newObj.foo); // false

4. 增加Set、Map、Date类型v-4

javascript
function deepClone(originValue) { // 判断是否是函数类型 if (originValue instanceof Function) { // 将函数转成字符串 let str = originValue.toString() // 截取函数体内容字符串 let subStr = str.substring(str.indexOf("{"), 1, str.lastIndexOf("}")) // 利用截取函数体内容的字符串和函数的构造器重新生成函数并返回 return new Function(subStr) } // 判断是否是Date类型 if(originValue instanceof Date) { return new Date(originValue.getTime()) } // 判断是否是Set类型(浅拷贝) if(originValue instanceof Set) { return new Set([...originValue]) } // 判断是否是Map类型(浅拷贝) if(originValue instanceof Map) { return new Map([...originValue]) } if(originValue == null && typeof originValue !== 'object') { return originValue } const newObj = Array.isArray(originValue) ? [] : {} for(const key in originValue) { // 不拷贝原型上的 if(originValue.hasOwnProterty(key)) { newObj[key] = deepClone(originValue[key]) } } return newObj }

测试

javascript
const obj = { name: 'zhangsan', address: { city: 'hangzhou' }, friends: ['zhangsan', 'lisi'], set: new Set([1,2,3]), map: new Map([['aaa',111], ['bbb', '222']]), createTime: new Date() } const newObj = deepClone(obj) console.log(newObj.set === obj.set) // false console.log(newObj.map === obj.map) // false console.log(newObj.createTime === obj.createTime) // false

5. 增加Symbol类型v-5

javascript
function deepClone(originValue) { // 判断是否是函数类型 if (originValue instanceof Function) { // 将函数转成字符串 let str = originValue.toString() // 截取函数体内容字符串 let subStr = str.substring(str.indexOf("{"), 1, str.lastIndexOf("}")) // 利用截取函数体内容的字符串和函数的构造器重新生成函数并返回 return new Function(subStr) } // 判断是否是Date类型 if(originValue instanceof Date) { return new Date(originValue.getTime()) } // 判断是否是Set类型(浅拷贝) if(originValue instanceof Set) { return new Set([...originValue]) } // 判断是否是Map类型(浅拷贝) if(originValue instanceof Map) { return new Map([...originValue]) } // 判断是否是Smybol类型 if(typeof originValue === 'symbol') { return Symbol(originValue.description) } if(originValue == null && typeof originValue !== 'object') { return originValue } const newObj = Array.isArray(originValue) ? [] : {} for(const key in originValue) { newObj[key] = deepClone(originValue[key]) } // 对key是Symbol做处理 const symbolKeys = Object.getOwnPropertySymbols(originValue) for(const key of symbolKeys) { // 不拷贝原型上的 if(originValue.hasOwnProterty(key)) { newObj[key] = deepClone(originValue[key]) } } return newObj }

测试

javascript
const s1 = Symbol('aaa') const s2 = Symbol('bbb') const obj = { name: 'zhangsan', address: { city: 'hangzhou' }, friends: ['zhangsan', 'lisi'], set: new Set([1,2,3]), map: new Map([['aaa',111], ['bbb', '222']]), createTime: new Date(), eating: function() { console.log(this.name + ' is eating') }, s1: s1, [s2]: {a: 1} } const newObj = deepClone(obj) console.log(obj.s1 === newObj.s1) // flase console.log(obj[s2] === newObj[s2]) // false

6. 增加循环引用(最终版)

javascript
// 参数中设置一个WeakMap用来保存 function deepClone(originValue, map = new WeakMap()) { // 判断是否是函数类型 if (originValue instanceof Function) { // 将函数转成字符串 let str = originValue.toString() // 截取函数体内容字符串 let subStr = str.substring(str.indexOf("{"), 1, str.lastIndexOf("}")) // 利用截取函数体内容的字符串和函数的构造器重新生成函数并返回 return new Function(subStr) } // 判断是否是Date类型 if(originValue instanceof Date) { return new Date(originValue.getTime()) } // 判断是否是Set类型(浅拷贝) if(originValue instanceof Set) { return new Set([...originValue]) } // 判断是否是Map类型(浅拷贝) if(originValue instanceof Map) { return new Map([...originValue]) } // 判断是否是Smybol类型 if(typeof originValue === 'symbol') { return Symbol(originValue.description) } if(originValue == null && typeof originValue !== 'object') { return originValue } // 判断之前是否存过,如果有则直接获取返回 if(map.has(originValue)) { return map.get(originValue) } const newObj = Array.isArray(originValue) ? [] : {} // 创建的newObj放到map里 map.set(originValue, newObj) for(const key in originValue) { newObj[key] = deepClone(originValue[key], map) } // 对key是Symbol做处理 const symbolKeys = Object.getOwnPropertySymbols(originValue) for(const key of symbolKeys) { // 不拷贝原型上的 if(originValue.hasOwnProterty(key)) { newObj[key] = deepClone(originValue[key], map) } } return newObj }

测试

javascript
const s1 = Symbol('aaa') const s2 = Symbol('bbb') const obj = { name: 'zhangsan', address: { city: 'hangzhou' }, friends: ['zhangsan', 'lisi'], set: new Set([1,2,3]), map: new Map([['aaa',111], ['bbb', '222']]), createTime: new Date(), eating: function() { console.log(this.name + ' is eating') }, s1: s1, [s2]: {a: 1} } obj.info= obj const newObj = deepClone(obj) console.log(newObj)

7. 另一种方法:MessageChannel + Promise

js
function deepClone(obj) { return new Promise((resolve) => { const { port1, port2 } = new MessageChannel(); port1.postMessage(obj); port2.onmessage = (msg) => { resolve(msg.data); }; }); } const obj = { a: { b: 1 }, }; deepClone(obj).then((res) => { console.log({ res: res, obj }); obj.a.b = 2; console.log({ res: res, obj }); });
如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:叶继伟

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!