title: 手写深拷贝
categories: Javascript
tag:
- 手写深拷贝
date: 2021-11-30 23:05:34
深拷贝函数
前面我们已经学习了对象相互赋值的一些关系,分别包括:
- 引入的赋值:指向同一个对象,相互之间会影响;
- 对象的浅拷贝:只是浅层的拷贝,内部引入对象时,依然会相互影响;
- 对象的深拷贝:两个对象不再有任何关系,不会相互影响;
前面我们已经可以通过一种方法来实现深拷贝了:JSON.parse
- 这种深拷贝的方式其实对于函数、Symbol 等是无法处理的;
- 并且如果存在对象的循环引用,也会报错的;
自定义深拷贝函数:
- 自定义深拷贝的基本功能;
- 对 Symbol 的 key 进行处理;
- 其他数据类型的值进程处理:数组、函数、Symbol、Set、Map;
- 对循环引用的处理;
深拷贝的实现
function isObject(value) {const valueType = typeof valuereturn value !== null && (valueType === 'object' || valueType === 'function')}function deepClone(originValue) {// 判断传入的originValue是否是一个对象类型if (!isObject(originValue)) {return originValue}const newObj = {}for (const key in originValue) {newObj[key] = deepClone(originValue[key])}return newObj}//测试代码=============================================================const obj = {name: 'why',age: 18,friend: {name: 'gwk',address: {city: '北京'}}}const newObj = deepClone(obj)obj.friend.name = 'dh'obj.friend.address.city = '杭州'console.log(obj, newObj)
处理数组类型
可以看到以上代码。处理数组类型的时候

因此,我们需要判断这里的 obj 应该是对象还是数组。

修改为以下代码

处理函数类型
如果是函数类型,直接复用

处理 Symbol 类型

- Symbol 类型作为 value 的时候,拷贝的时候,会引用之前的。因此也需要对 Symbol 类型进行处理

- Symbol 类型为 key 的时候,我们发现是不能拷贝的。因为 Symbol 类型是不能遍历的。


处理 Set 类型
当我们的原始对象有 Set 数据类型的属性时


处理 Map 类型

和 Set 数据类型类似

此时经过以上处理,最终的深拷贝函数为:
function isObject(value) {const valueType = typeof valuereturn value !== null && (valueType === 'object' || valueType === 'function')}function deepClone(originValue) {//判断是否是Map类型if (originValue instanceof Set) {return new Set([...originValue])}//判断是否是Set类型if (originValue instanceof Map) {return new Map([...originValue])}// 判断如果是Symbol,就创建新的Symbolif (typeof originValue === 'symbol') {return Symbol(originValue.description)}//判断是否是函数if (typeof originValue === 'function') {// 判断传入的originValue是否是函数类型return originValue}// 判断传入的originValue是否是一个对象类型if (!isObject(originValue)) {return originValue}const newObj = Array.isArray(originValue) ? [] : {}for (const key in originValue) {newObj[key] = deepClone(originValue[key])}// 对Symbol的key进行特殊处理const symbolsKeys = Object.getOwnPropertySymbols(originValue)for (const sKey of symbolsKeys) {newObj[sKey] = deepClone(originValue[sKey])}return newObj}//测试代码========================================let s1 = Symbol('aaa')let s2 = Symbol('bbb')const obj = {name: 'why',age: 18,friend: {name: 'gwk',address: {city: '北京'}},hobby: ['篮球', '足球'],foo: function () {console.log('foo')},[s1]: 'aaa',s2: s2,set: new Set(['aaa', 'bbb', 'ccc']),map: new Map([['aaa', 'bbb'],['bbb', 'ccc']])}const newObj = deepClone(obj)obj.friend.name = 'dh'obj.friend.address.city = '杭州'console.log(obj, newObj)console.log(obj.s2 === newObj.s2)
