Object.keys(obj) :返回自身的所有可枚举属性
Object.getOwnPropertyNames(obj) :返回自身的所有属性,不论是否可枚举
for...in :迭代一个对象的可枚举属性,包括继承的可枚举属性
obj.hasOwnProperty(prop) :返回布尔值,表实例对象自身是否具有该属性(不论是否为symbol和可枚举)

只考虑普通对象
const isObject = (target) => (typeof target === 'object' || typeof target === 'function') && target !== null; // 判断是否为非null对象function deepClone(target, map = new WeakMap()) {if(!isObject(target)) // 不是 非null对象 直接返回return target;// 获取其原型let ctor = target.constructor;let cloneTarget = new ctor();if(map.get(target)) // 避免死循环 eg.环形链表return target;map.set(target, true);for (const prop of Object.getOwnPropertyNames(target)) {cloneTarget[prop] = deepClone(target[prop], map); // 递归处理 深度拷贝属性}for (const prop of Object.getOwnPropertySymbols(target)) { // 考虑SymbolcloneTarget[prop] = deepClone(target[prop], map);}// for (const prop of Reflect.ownKeys(target)) {// cloneTarget[prop] = deepClone(target[prop], map);// }return cloneTarget;}
考虑更多类型
const getType = obj => Object.prototype.toString.call(obj); // 根据toString方法转成的字符串来判断类型const isObject = (target) => (typeof target === 'object' || typeof target === 'function') && target !== null; // 判断是否为非null对象const canTraverse = { // 可遍历的类型'[object Map]': true,'[object Set]': true,'[object Array]': true,'[object Object]': true,'[object Arguments]': true,};const mapTag = '[object Map]';const setTag = '[object Set]';const boolTag = '[object Boolean]';const numberTag = '[object Number]';const stringTag = '[object String]';const symbolTag = '[object Symbol]';const dateTag = '[object Date]';const errorTag = '[object Error]';const regexpTag = '[object RegExp]';const funcTag = '[object Function]';const handleRegExp = (target) => { // 处理正则const { source, flags } = target; // source正则模式的字符串形式 flags修饰符return new target.constructor(source, flags);}const handleFunc = (func) => { // 处理函数if(!func.prototype) return func; // 箭头函数直接返回自身const bodyReg = /(?<={)(.|\n)+(?=})/; // 函数体正则匹配const paramReg = /(?<=\()(.|\n)+(?=\)\s+\{)/; // 参数正则匹配const funcString = func.toString();// 分别匹配 函数参数 和 函数体const param = paramReg.exec(funcString);const body = bodyReg.exec(funcString);if(!body) return null; // 未匹配成功会返回 null -> falseif (param) { // 是否有参数const paramArr = param[0].split(',');return new Function(...paramArr, body[0]); // Function构造函数的最后一个参数才会被当作是函数体,前面的都被当作参数} else {return new Function(body[0]);}}const handleNotTraverse = (target, tag) => { // 处理不可遍历的类型const Ctor = target.constructor; // 直接获取其构造函数switch(tag) {case boolTag:return new Object(Boolean.prototype.valueOf.call(target));case numberTag:return new Object(Number.prototype.valueOf.call(target));case stringTag:return new Object(String.prototype.valueOf.call(target));case symbolTag:return new Object(Symbol.prototype.valueOf.call(target));case errorTag:case dateTag:return new Ctor(target);case regexpTag:return handleRegExp(target);case funcTag:return handleFunc(target);default:return new Ctor(target);}}const deepClone = (target, map = new WeakMap()) => {if(!isObject(target)) // 不是 非null对象 直接返回return target;const type = getType(target); // 获取类型字符串let cloneTarget; // 存储结果if(!canTraverse[type]) {// 处理不能遍历的对象return handleNotTraverse(target, type);} else {// 处理可遍历对象 拷贝获取其原型let ctor = target.constructor;cloneTarget = new ctor();}if(map.get(target)) // 避免死循环 eg.环形链表return target;map.set(target, true);if(type === mapTag) {//处理Maptarget.forEach((item, key) => {// (键值都进行拷贝处理 这是因为map的键值都可为任意类型)cloneTarget.set(deepClone(key, map), deepClone(item, map)); // 递归处理})}if(type === setTag) {//处理Settarget.forEach(item => {cloneTarget.add(deepClone(item, map)); // 递归处理})}// 处理数组和对象// for...in 可处理对象的可枚举属性 会包括继承的属性// Object.keys() 可处理对象的可枚举属性 只包括自身的属性// Object.getOwnPropertyNames() 处理自身属性 无论是否可枚举// 上面的方法都无法处理 属性名为Symbol类型的属性// Object.getOwnPropertySymbols()// Reflect.ownKeys()for (const prop of Object.getOwnPropertyNames(target)) {cloneTarget[prop] = deepClone(target[prop], map); // 递归处理 深度拷贝属性}for (const prop of Object.getOwnPropertySymbols(target)) { // 考虑SymbolcloneTarget[prop] = deepClone(target[prop], map);}// for (const prop of Reflect.ownKeys(target)) {// cloneTarget[prop] = deepClone(target[prop], map);// }return cloneTarget;}
测试:
// 对象const obj = {a: 1,b: {c: 123,d: 465,e: {f: 5050}}}Object.defineProperty(obj, 'prop', {value: '不可枚举',enumerable: false,configurable: true})const prop = Symbol(123);obj[prop] = 'Symbol属性名';const newObj = deepClone(obj);// console.log('newObj: ', newObj);// obj.a = 456// obj.b.c = 777;// obj[prop] = 'Symbol更改';// console.log('newObj: ', newObj);const objTest = {prop: 123}objTest.next = objTest;// console.log('objTest: ', objTest);deepClone(objTest)// console.log('deepClone(objTest): ', deepClone(objTest));const arr = [1,2,3,obj];const newArr = deepClone(arr);// console.log('newArr: ', newArr);// arr[3].a = 456;// arr[3].b.c = 777;// console.log('newArr: ', newArr);// mapconst map = new Map();map.set('obj', obj);map.set('arr', arr);const newMap = deepClone(map);// console.log('newMap: ', newMap);// map.get('arr')[3].a = 456;// map.get('arr')[3].b.c = 777;// console.log('newMap: ', newMap);// setconst set = new Set(arr);const newSet = deepClone(set);// console.log('newSet: ', newSet);// set.add(789);// newSet.add('ABC');// console.log('newSet: ', newSet);// functionlet func = function (val) {return val + '456'};const test = func;const newFunc = deepClone(func);// console.log('func.name: ', func.name); // func// console.log('test.name: ', test.name); // func// console.log('newFunc.name: ', newFunc.name); // anonymous// console.log('newFunc(123): ', newFunc(123)); // 123456// 箭头函数const arrowFunc = (val) => val + '123';const testArrow = arrowFunc;const newArrowFunc = deepClone(arrowFunc);console.log('testArrow === arrowFunc: ', testArrow === arrowFunc); // trueconsole.log('newArrowFunc === arrowFunc: ', newArrowFunc === arrowFunc); // true(返回本身)console.log('newArrowFunc(456): ', newArrowFunc(456));// Dateconst date = new Date();const newDate = deepClone(date);console.log('date: ', date);console.log('newDate: ', newDate);console.log('newDate.getDay(): ', newDate.getDay());
