1、常见的一种实现方式
const info = JSON.parse(JSON.stringify(obj))
2、简单实现 deepClone
//工具函数 function isObject(value) { const type = typeof value return (value !== null) && (type === 'object' || type === 'function') } function deepClone(originValue) { //1.判断传入的originValue是不是一个对象类型 if (!isObject(originValue)) return originValue //2.简单实现 const newObj = {} for (const key in originValue) { newObj[key] = deepClone(originValue[key]) } return newObj } //测试一下 const obj = { name: '当地小有名气可爱鬼', height: 170, eat: { first: '臭豆腐', second: '榴莲' } } const test = deepClone(obj) console.log(test === obj); //false obj.eat.first = '螺蛳粉' console.log(test); //{ name: '当地小有名气可爱鬼', height: 170, eat: { first: '臭豆腐', second: '榴莲' } }
3、增加功能 - 处理其他数据类型
//工具函数 function isObject(value) { const type = typeof value return (value !== null) && (type === 'object' || type === 'function') } function deepClone(originValue) { //类型判断 // 判断是否是一个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 //判断传入的对象是数组,还是对象 const newObject = Array.isArray(originValue) ? [] : {} for (const key in originValue) { newObject[key] = deepClone(originValue[key]) } //处理一下symbol的key const symbolKeys = Object.getOwnPropertySymbols(originValue) for (const iterator of symbolKeys) { newObject[iterator] = deepClone(originValue[iterator]) } return newObject } //测试一下 let s1 = Symbol('s1') let s2 = Symbol('s2') const info = { name: '当地小有名气可爱鬼', height: 170, eat: { first: '臭豆腐', second: '榴莲' }, singer: ['周杰伦', '孙燕姿', '邓紫棋'], //symbol 可以做key\value [s1]: '我是s1', s2: s2, bar: function () { return '我是函数' }, set: new Set(['1', '2', '3']), map: new Map([ ['a', 'b'], ['c', 'd'] ]) } const check = deepClone(info) console.log(check === info); //false info.eat.first = '小龙虾' console.log(info, '我是info'); // { // name: '当地小有名气可爱鬼', // height: 170, // eat: { // first: '小龙虾', // second: '榴莲' // }, // singer: ['周杰伦', '孙燕姿', '邓紫棋'], // s2: Symbol(s2), // bar: [Function: bar], // set: Set(3) { // '1', // '2', // '3' // }, // map: Map(2) { // 'a' => 'b', 'c' => 'd' // }, // [Symbol(s1)]: '我是s1' // } // 我是info console.log(check, '我是check'); // { // name: '当地小有名气可爱鬼', // height: 170, // eat: { // first: '臭豆腐', // second: '榴莲' // }, // singer: ['周杰伦', '孙燕姿', '邓紫棋'], // s2: Symbol(s2), // bar: [Function: bar], // set: Set(3) { // '1', // '2', // '3' // }, // map: Map(2) { // 'a' => 'b', 'c' => 'd' // }, // [Symbol(s1)]: '我是s1' // } // 我是check console.log(check.s1 === info.s1);//true console.log(check.s2 === info.s2);//false
4、增加功能 - 解决循环引用的问题(WeakMap)
function isObject(value) { const type = typeof value return (value !== null) && (type === 'object' || type === '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) console.log(symbolKeys, 'symbolKeys'); for (const iterator of symbolKeys) { newObject[iterator] = deepClone(originValue[iterator],map) } return newObject } //测试一下: let s1 = Symbol('s1') let s2 = Symbol('s2') const info = { name: '当地小有名气可爱鬼', height: 170, eat: { first: '臭豆腐', second: '榴莲' }, singer: ['周杰伦', '孙燕姿', '邓紫棋'], //symbol 可以做key\value [s1]: '我是s1', s2: s2, bar: function () { return '我是函数' }, set: new Set(['1', '2', '3']), map: new Map([ ['a', 'b'], ['c', 'd'] ]) } info.list = info //设置一个引用,如果不做处理会报栈溢出: Maximum call stack size exceeded const check = deepClone(info) console.log(check === info); //false info.eat.first = '小龙虾' console.log(info); console.log(check.s2 === info.s2)
简单拓展一下这里为什么使用weakMap:
1.Map可以使用任意类型的key值,不限字符串,对象等
2.WeakMap只能使用对象作为key值,是弱引用,当从WeakMap中移除时,会自动垃圾回收