在业务中常常会遇到须要对模板数据/初始数据进行加工处理,并且,该数据可能能须要在多处被复用。面试
简单粗暴更改原始数据的作法,会污染其余依赖项,因此咱们须要对原始数据进行一份深拷贝,保持各个依赖项数据的独立性,作到既能够复用,又不互相污染。segmentfault
也许咱们用到过Object.assign()进行对象合并,亦或者用数组的slice(), concat()方法对数组进行复制,但这几种方法都不是深拷贝,是浅拷贝。数组
简言之,深拷贝就是每一个对象都是独立的,独立意味着拥有各自的独立内存空间;而浅拷贝意味着拥有公共的引用空间。函数
因此,值类型的数据不存在浅拷贝的问题,只有引用数据类型才存在。愿更深刻的了解深拷贝和浅拷贝的,能够参考文章最末尾的「参考文章」。post
let recursiveClone = val => Array.isArray(val) ? Array.from(val, recursiveClone) : val;
概述JS的数据类型,从大体上分为两种:prototype
对于基本数据类型,不存在深拷贝的问题,由于它们是值类型的数据。值类型的数据存放在栈内存中,从新赋值就是独立的。code
而对于众多的引用数据类型,须要分别进行处理,集中处理object和array,这也是咱们在业务中遇到最多的状况。对象
const deepCloneTypes = ['Object', 'Array', 'Map', 'Set']; function isObject(source) { const type = typeof source; return source !== null && (type === 'object' || type === 'function') } function getType(source) { return (Object.prototype.toString.call(source)).split(' ')[1].slice(0, -1) } function processOtherType(source) { const Ctor = source.constructor; return new Ctor(source); } function processFunctionType (source) { let _source = source.toString(); // 区分是不是箭头函数 if (source.prototype) { // 若是有prototype就是普通函数 let argsReg = /function\s*\w*\(([^\)]*)\)/; let bodyReg = /\{([\s\S]*)\}/; let fnArgs = (argsReg.exec(source))[1]; let fnBody = (bodyReg.exec(source))[1]; console.log(fnArgs, fnBody); return new Function (fnArgs, fnBody) } else { // 箭头函数没有prototype return eval(_source) } } function deepClone (source, map = new WeakMap()) { // 首先用typeof来筛选是不是引用数据类型,若是连引用数据类型都不是,那么就判断是基本数据类型,直接返回便可 if (!isObject(source)) { return source } const type = getType(source); let cloneTarget; // 防止循环引用 if (map.get(source)) { return map.get(source); } map.set(source, cloneTarget); // 接下来判断是不是须要进行循环拷贝的引用数据类型,诸如new Boolean, new Number这样的,也不须要循环拷贝 if (!deepCloneTypes.includes(type)) { cloneTarget = processOtherType(source); } else { cloneTarget = new source.constructor(); // return Object.create(source.constructor.prototype) //不能这样,这样是创造了一个对象 if (type === 'Object' || type === 'Array') { let keys = type === 'Object' ? Object.keys(source) : undefined; // 若是支持optional chaining的话能够写成?. (keys || source).forEach((val, key) => { if (keys) { key = val; } cloneTarget[key] = deepClone(source[key], map); //在这里进行递归调用 }) } if (type === 'Function') { cloneTarget = processFunctionType(source) } if (type === 'Map') { source.forEach((val, key) => { cloneTarget.set(key, deepClone(val, map)) }) } if (type === 'Set') { source.forEach((val, key) => { cloneTarget.add(deepClone(val, map)) }) } } return cloneTarget }
参考:递归