module.exports = clone; /* Deep clones all properties except functions var arr = [1, 2, 3]; var subObj = {aa: 1}; var obj = {a: 3, b: 5, c: arr, d: subObj}; var objClone = clone(obj); arr.push(4); subObj.bb = 2; obj; // {a: 3, b: 5, c: [1, 2, 3, 4], d: {aa: 1}} objClone; // {a: 3, b: 5, c: [1, 2, 3], d: {aa: 1, bb: 2}} */ function clone(obj) { let result = obj; var type = {}.toString.call(obj).slice(8, -1); if (type == 'Set') { return new Set([...obj].map(value => clone(value))); } if (type == 'Map') { return new Map([...obj].map(kv => [clone(kv[0]), clone(kv[1])])); } if (type == 'Date') { return new Date(obj.getTime()); } if (type == 'RegExp') { return RegExp(obj.source, getRegExpFlags(obj)); } if (type == 'Array' || type == 'Object') { result = Array.isArray(obj) ? [] : {}; for (var key in obj) { // include prototype properties result[key] = clone(obj[key]); } } // primitives and non-supported objects (e.g. functions) land here return result; } function getRegExpFlags(regExp) { if (typeof regExp.source.flags == 'string') { return regExp.source.flags; } else { var flags = []; regExp.global && flags.push('g'); regExp.ignoreCase && flags.push('i'); regExp.multiline && flags.push('m'); regExp.sticky && flags.push('y'); regExp.unicode && flags.push('u'); return flags.join(''); } }