JavaScript 中深拷贝实现
拷贝时候涉及到:
1、循环结构
2、判断数组 Array 还是对象 Object
函数实现
/** * 获取满足条件的数组中的第一个元素 * @param {Array} list 将要筛选的数组 * @param {Function} f 用来过滤的函数 */ function find(list, f) { return list.filter(f)[0] } /** * 深拷贝对象,同时考虑到了循环引用的情况 * 缓存了所有的嵌套对象和它的拷贝 * 如果检测到循环引用,返回拷贝,防止了无限循环 * @param {Object} obj 需要拷贝的对象 * @param {Array} cache 用来判断是否循环引用,存储对象以及对象的拷贝 */ function deepCopy(obj, cache = []) { // 为空或者不是对象则返回原 obj if (obj === null || typeof obj !== 'object') { return obj } // 若是循环结构,则返回之前对象的 copy,而不是引用 const hit = find(cache, c => c.original === obj) if (hit) { return hit.copy } const copy = Array.isArray(obj) ? [] : {} // 将 copy 放入 cache 中 // 我们可能在递归中引用 copy cache.push({ original: obj, copy }) Object.keys(obj).forEach(key => { copy[key] = deepCopy(obj[key], cache) }) return copy }
应用
1、非嵌套例子:
const original = { a: 1, b: 'string', c: true, d: null, e: undefined } const copy = deepCopy(original) console.log('copy === original :', copy === original) // copy === original : false console.log('copy :', JSON.stringify(copy, null, 2)) // copy : { // "a": 1, // "b": "string", // "c": true, // "d": null // }
2、嵌套例子:
const original = { a: { b: 1, c: [ 2, 3, { d: 4 } ] } } const copy = deepCopy(original) console.log('copy === original :', copy === original) // copy === original : false console.log('copy :', JSON.stringify(copy, null, 2)) // copy : { // "a": { // "b": 1, // "c": [ // 2, // 3, // { // "d": 4 // } // ] // } // }
3、循环引用
const original = { a: 1 } original.circularExample = original const copy = deepCopy(original) console.log('copy === original :', copy === original) // copy === original : false console.log('copy :', copy) // 这里循环引用不可使用 JSON.stringify 来转换,会报错 // copy : { a: 1, circularExample: [Circular] }