以下是浅拷贝与深拷贝的各种方法及其区别:
测试数据:
const target = { a: '1', e: [1, 2, 3, 4, [5, 6, [7, 8, [9]]]], b: { c: [{ h: '1', i: '2' }], d: { f: { g: '4' } } }, j: null, k: undefined, l: function () { console.log('l'); }, date: new Date(), reg: /[A-Z]/g, }
所需知识点:
typeof [],null,object 皆为object
hasOwnProperty:过滤原型属性,只能获取自有属性
对象遍历:
1、for in可以遍历可枚举属性,包括原型链上的属性;
2、Object.keys返回一个由对象可枚举属性组成的数组,不包括原型属性;
3、Object.getOwnProperty 返回对象的自有属性数组,包括可枚举的和不可枚举的,不包括原型属性
数组遍历:
for in 输出数组的index,即'0','1','2','3','4','5'....
Object.keys 同上
Object.getOwnProperty 输出'0','1','2','3'...,'length'
浅克隆
const shallClone = (target) => { if (typeof target === 'object' && target !== null) { const cloneTarget = Array.isArray(target) ? [] : {} for (prop in target) { if (target.hasOwnProperty(prop)) { cloneTarget[prop] = target[prop] } } return cloneTarget } else { return target } } // console.log('shallClone', shallClone(target)); // target.b = {} //此层拷贝的是值 // target.b.c = {} //此层拷贝的是引用 // target.b.c.h = '7' //此层拷贝的是引用
经测试,shallClone方法除函数拷贝后是个空函数外基本上可以成功拷贝,但是对象值的对象值只拷贝了引用,而不是值,即对象只拷贝了一层。
深克隆
1、第一种方法
function deepClone(target) { if (typeof target === 'object' && target !== null) { const cloneTarget = Array.isArray(target) ? [] : {} for (let prop in target) { if (target.hasOwnProperty(prop)) { cloneTarget[prop] = deepClone(target[prop]) } } return cloneTarget } else { return target } } console.log('deepClone', deepClone(target)); // target.b = {} //此层拷贝的是值 // target.b.c = {} //此层拷贝的是值 // target.b.c.h = '7' //此层拷贝的是值
运行结果:
经测试,deepClone方法可以拷贝整个对象的值,而不是引用,但是date格式和正则表达式拷贝失败,结果为空对象,函数拷贝失败,为空函数。
2、第二种方法
const isObject = (target) => (typeof target === 'object') && target !== null; function deepClone2(target, map = new Map()) { Date.prototype.clone = function () { return new Date(this.valueOf()); } RegExp.prototype.clone = function () { var pattern = this.valueOf(); var flags = ''; flags += pattern.global ? 'g' : ''; flags += pattern.ignoreCase ? 'i' : ''; flags += pattern.multiline ? 'm' : ''; return new RegExp(pattern.source, flags); }; // 先判断该引用类型是否被 拷贝过 if (map.get(target)) { return target; } // 检测当前对象target是否与 正则、日期格式对象匹配 if (eval(target) instanceof RegExp || target instanceof Date) { console.log(target.clone()) return target.clone() } if (isObject(target)) { map.set(target, true); // 为循环引用的对象做标记 const cloneTarget = Array.isArray(target) ? [] : {}; for (let prop in target) { if (target.hasOwnProperty(prop)) { cloneTarget[prop] = deepClone2(target[prop], map); } } return cloneTarget; } else { return target; } } console.log('deepClone2', deepClone2(target)); // target.b = {} //此层拷贝的是值 // target.b.c = {} //此层拷贝的是值 // target.b.c.h = '7' //此层拷贝的是值
运行结果:
经测试,除函数外都能拷贝成功。
3、第三种方法
console.log('deepClone3', deepClone3(target)); // target.b = {} //此层拷贝的是值 // target.b.c = {} //此层拷贝的是值 // target.b.c.h = '7' //此层拷贝的是值
运行结果:
经测试,除开undefined值和正则,函数不能拷贝,其他情况都能拷贝。