鄙人老版 js 深拷贝博客链接,当时写的存在很多不足...现在跟着方应杭老师复习了下,收获满满。
const b = JSON.parse(JSON.stringify(a))
缺点:
不支持 Date、RegExp(正则)、函数等数据;
不支持引用(即环状结构,类似 window.self = window)。
支持 Date、RegExp(正则)、函数等引用数据的拷贝。
const deepClone = (a) => { let res = undefined // 数据类型判断 if (a instanceof Object) { // 类 判断 if (a instanceof Function) { // 箭头函数判断 if (a.prototype) { // 普通函数 res = function (...args) { return a.call(this, ...args) } } else { // 箭头函数 res = (...args) => { return a.call(undefined, ...args) } } } else if (a instanceof Array) { res = [] } else if (a instanceof Date) { res = new Date(a - 0) // 日期格式 - 0 自动转换为时间戳 } else if (a instanceof RegExp) { res = new RegExp(a) } else { res = {} } // 递归 for (let k in a) { if (a.hasOwnProperty(k)) { res[k] = deepClone(a[k]) } } } else { res = a } return res }
测试结果:
比如浏览器的 window.self = window,这个时候如果还用上面的拷贝就会导致无限递归导致栈溢出报错。
正确的做法是添加一个 map 来记录每次拷贝过的数据,如果出现重复的就不再进行拷贝和递归。(ps:为什么用 map?因为对象只能 key 值只能为字符串)
const deepClone = (a, cache) => { let res = undefined if(!cache){ cache = new Map() // 缓存不能全局,最好临时创建并递归传递 } if (a instanceof Object) { // 每次拷贝前判断前面是否已经拷贝过 // 如果出现 a.self = a 在这里就会返回 // 防止后续无限递归导致栈溢出 if (cache.get(a)) { return cache.get(a) } if (a instanceof Function) { if (a.prototype) { res = function (...args) { return a.call(this, ...args) } } else { res = (...args) => { return a.call(undefined, ...args) } } } else if (a instanceof Array) { res = [] } else if (a instanceof Date) { res = new Date(a - 0) } else if (a instanceof RegExp) { res = new RegExp(a) } else { res = {} } // 每次递归前将拷贝的值就存入 map cache.set(a, res) for (let k in a) { if (a.hasOwnProperty(k)) { // 通过参数传递缓存 map res[k] = deepClone(a[k], cache) } } } else { res = a } return res }
测试结果: