c++实现polyline方法_对象的深浅克隆方法实现

本文介绍了C++中实现polyline方法,并详细探讨了对象的浅克隆与深克隆。通过代码示例展示了浅克隆存在的问题,以及如何使用JSON.parse和自定义函数实现深克隆,同时指出深克隆在处理特殊对象和循环引用时的挑战。
摘要由CSDN通过智能技术生成
53657eb690c97ba4859f216759e64a0e.png

废话不多说,直入主题,直接上代码。

一、浅克隆

  浅克隆之所以被称为浅克隆,是因为对象只会被克隆最外部的一层,至于更深层的对象,则依然是通过引用指向同一块堆内存.

function shallowClone(o) { const obj = {}; for ( let i in o) { obj[i] = o[i]; } return obj;}// 被克隆对象const oldObj = { a: 1, b: [ 'e', 'f', 'g' ], c: { h: { i: 2 } }};const newObj = shallowClone(oldObj);console.log(newObj.c.h, oldObj.c.h); // { i: 2 } { i: 2 }console.log(oldObj.c.h === newObj.c.h); // true

我们可以看到,很明显虽然oldObj.c.h被克隆了,但是它还与oldObj.c.h相等,这表明他们依然指向同一段堆内存,这就造成了如果对newObj.c.h进行修改,也会影响oldObj.c.h,这就不是一版好的克隆。

newObj.c.h.i = 'change';console.log(newObj.c.h, oldObj.c.h); // { i: 'change' } { i: 'change' }

我们改变了newObj.c.h.i的值,oldObj.c.h.i也被改变了,这就是浅克隆的问题所在.

当然有一个新的apiObject.assign()也可以实现浅复制,但是效果跟上面没有差别,所以我们不再细说了.

二、深克隆

2.1 JSON.parse方法

前几年微博上流传着一个传说中最便捷实现深克隆的方法,

JSON对象parse方法可以将JSON字符串反序列化成JS对象,stringify方法可以将JS对象序列化成JSON字符串,这两个方法结合起来就能产生一个便捷的深克隆.

const oldObj = { a: 1, b: [ 'e', 'f', 'g' ], c: { h: { i: 2 } }};const newObj = JSON.parse(JSON.stringify(oldObj));console.log(newObj.c.h, oldObj.c.h); // { i: 2 } { i: 2 }console.log(oldObj.c.h === newObj.c.h); // falsenewObj.c.h.i = 'change';console.log(newObj.c.h, oldObj.c.h); // { i: 'change' } { i: 2 }

果然,这是一个实现深克隆的好方法,但是这个解决办法是不是太过简单了.

确实,这个方法虽然可以解决绝大部分是使用场景,但是却有很多坑:

  1. 他无法实现对函数 、RegExp等特殊对象的克隆。
  2. 会抛弃对象的constructor,所有的构造函数会指向Object。
  3. 对象有循环引用,会报错。

上代码看一下这几个坑:

// 构造函数function person(pname) { this.name = pname;}const Messi = new person('Messi');// 函数function say() { console.log('hi');};const oldObj = { a: say, b: new Array(1), c: new RegExp('ab+c', 'i'), d: Messi};const newObj = JSON.parse(JSON.stringify(oldObj));// 无法复制函数console.log(newObj.a, oldObj.a); // undefined [Function: say]// 稀疏数组复制错误console.log(newObj.b[0], oldObj.b[0]); // null undefined// 无法复制正则对象console.log(newObj.c, oldObj.c); // {} /ab+c/i// 构造函数指向错误console.log(newObj.d.constructor, oldObj.d.constructor); // [Function: Object] [Function: person]

我们可以看到在对函数、正则对象、稀疏数组等对象克隆时会发生意外,构造函数指向也会发生错误。

const oldObj = {};对象的循环引用会抛出错误.oldObj.a = oldObj;const newObj = JSON.parse(JSON.stringify(oldObj));console.log(newObj.a, oldObj.a); // TypeError: Converting circular structure to JSON

对象的循环引用会抛出错误.。

2.2实现一个深拷贝函数

如果要避免上面的问题,那我们需要对不同的对象(正则、数组、Date等)要采用不同的处理方式,我们需要实现一个对象类型判断函数。

const isType = (obj, type) => { if (typeof obj !== 'object') return false; const typeString = Object.prototype.toString.call(obj); let flag; switch (type) { case 'Array': flag = typeString === '[object Array]'; break; case 'Date': flag = typeString === '[object Date]'; break; case 'RegExp': flag = typeString === '[object RegExp]'; break; default: flag = false; } return flag;};

测试代码:

const arr = Array.of(3, 4, 5, 2);console.log(isType(arr, 'Array')); // true

做好了这些准备工作,我们就可以进行深克隆的实现了。下面直接上代码:

const clone = parent => { // 维护两个储存循环引用的数组 const parents = []; const children = []; const _clone = parent => { if (parent === null) return null; if (typeof parent !== 'object') return parent; let child, proto; if (isType(parent, 'Array')) { // 对数组做特殊处理 child = []; } else if (isType(parent, 'RegExp')) { // 对正则对象做特殊处理 child = new RegExp(parent.source, getRegExp(parent)); if (parent.lastIndex) child.lastIndex = parent.lastIndex; } else if (isType(parent, 'Date')) { // 对Date对象做特殊处理 child = new Date(parent.getTime()); } else { // 处理对象原型 proto = Object.getPrototypeOf(parent); // 利用Object.create切断原型链 child = Object.create(proto); } // 处理循环引用 const index = parents.indexOf(parent); if (index != -1) { // 如果父数组存在本对象,说明之前已经被引用过,直接返回此对象 return children[index]; } parents.push(parent); children.push(child); for (let i in parent) { // 递归 child[i] = _clone(parent[i]); } return child; }; return _clone(parent);};

我们做个测试:

function person(pname) { this.name = pname;}const Messi = new person('Messi');function say() { console.log('hi');}const oldObj = { a: say, c: new RegExp('ab+c', 'i'), d: Messi,};oldObj.b = oldObj;const newObj = clone(oldObj);console.log(newObj.a, oldObj.a); // [Function: say] [Function: say]console.log(newObj.b, oldObj.b); // { a: [Function: say], c: /ab+c/i, d: person { name: 'Messi' }, b: [Circular] } { a: [Function: say], c: /ab+c/i, d: person { name: 'Messi' }, b: [Circular] }console.log(newObj.c, oldObj.c); // /ab+c/i /ab+c/iconsole.log(newObj.d.constructor, oldObj.d.constructor); // [Function: person] [Function: person]

总结

这个深克隆函数能满足大部分的日常需求,还不算完美,例如Buffer对象、Promise、Set、Map可能都需要我们做特殊处理,另外对于确保没有循环引用的对象,我们可以省去对循环引用的特殊处理,因为这很消耗时间,不过一个基本的深克隆函数我们已经实现了。

如果在生产环境中最好用npm上的lodash的深克隆实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值