在咱们平常工做中常常会遇到须要去克隆一个对象好比多个地方用到的公共的图表基本参数的配置
相信不少人会想到用 Object.assign
, JSON.stringify
和 JSON.parse
方法去克隆一个对象,这个能够明确告诉你们这些都是些不靠谱的浅度克隆。javascript
Object.assign
在控制台执行下列操做你们有没有发现联动了。关于此方法具体请参考文档
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assignjava
JSON.stringify
和 JSON.parse
克隆对象,一样在控制输入你们有没有发现什么异常?虽然 JSON.stringify(value[, replacer[, space]])
能够处理可是太麻烦了,这个方法我就很少说了具体仍是参考文档
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringifysegmentfault
function isArray (arr) { return Object.prototype.toString.call(arr) === '[object Array]'; } // 深度克隆 function deepClone (obj) { if(typeof obj !== "object" && typeof obj !== 'function') { return obj; //原始类型直接返回 } var o = isArray(obj) ? [] : {}; for(i in obj) { if(obj.hasOwnProperty(i)){ o[i] = typeof obj[i] === "object" ? deepClone(obj[i]) : obj[i]; } } return o; }
看是靠谱是真是假咱们来验证一把 仍是在控制台输入数组
你们发现什么异常的了吗?浏览器
这个没有区分具体的对象,在此问下你们js的对象有哪些呢?相信通常人答不出来4个[object Object]
, [object Array]
, [object Null]
, [object RegExp]
, [object Date]
, [object HTMLXXElement]
, [object Map]
,[object Set]
,...
等等一系列babel
检测类型使用 Object.prototype.toString.call(xxx)
和 typeof
this
咱们分析下上面对象中哪些是引用类型须要特殊处理呢?相信你们都不陌生了。[object Object]
和 [object Array]
spa
好!详细你们思路有了,咋们用递归来实现一把吧!prototype
const deepClone = function(obj) { // 先检测是否是数组和Object // let isMap = Object.prototype.toString.call(obj) === '[object Map]; // let isSet = Object.prototype.toString.call(obj) === '[object Set]; // let isArr = Object.prototype.toString.call(obj) === '[object Array]'; let isArr = Array.isArray(obj); let isJson = Object.prototype.toString.call(obj) === '[object Object]'; if (isArr) { // 克隆数组 let newObj = []; for (let i = 0; i < obj.length; i++) { newObj[i] = deepClone(obj[i]); } return newObj; } else if (isJson) { // 克隆Object let newObj = {}; for (let i in obj) { newObj[i] = deepClone(obj[i]); } return newObj; } // 不是引用类型直接返回 return obj; }; Object.prototype.deepClone = function() { return deepClone(this); };
咋们先不考虑Map
Set
Arguments
[object XXArrayBuffer]
对象了原理都是同样
各类状况分析完了才说算是真克隆
咱们在控制台看下3d
是否是解决了? 在此并无结束。 专一的伙伴们相信发现了对象中包含了个 deepClone
方法,具体细节咱们在此就很少说了,咱们给 Object
添加了个 Object.prototype.deepClone
方法致使了每一个对象都有了此方法。
原则上咱们不容许在原型链上添加方法的,由于在循环中 for in
, Object.entries
, Object.values
, Object.keys
等方法会出现自定义的方法。
相信熟悉 Object
文档的伙伴人已经知道解决方案了,
Object.defineProperty
这个方法给你们带来了福音 具体参考 Object 文档。咱们使用一个enumerable
(不可枚举)属性就能够解决了。
在原来基础上添加如下代码便可。
Object.defineProperty(Object.prototype, 'deepClone', {enumerable: false});
在看控制台
一样上面方法中也是没法克隆一个不可枚举的属性。
完整代码以下
const deepClone = function(obj) { // 先检测是否是数组和Object // let isArr = Object.prototype.toString.call(obj) === '[object Array]'; let isArr = Array.isArray(obj); let isJson = Object.prototype.toString.call(obj) === '[object Object]'; if (isArr) { // 克隆数组 let newObj = []; for (let i = 0; i < obj.length; i++) { newObj[i] = deepClone(obj[i]); } return newObj; } else if (isJson) { // 克隆Object let newObj = {}; for (let i in obj) { newObj[i] = deepClone(obj[i]); } return newObj; } // 不是引用类型直接返回 return obj; }; Object.prototype.deepClone = function() { return deepClone(this); }; Object.defineProperty(Object.prototype, 'deepClone', {enumerable: false});
为了兼容低版本浏览器须要借助
babel-polyfill
;
好了,深度克隆介绍到此。