Skip to content

Commit

Permalink
refactor(deepClone)
Browse files Browse the repository at this point in the history
  • Loading branch information
lv-z-l committed Sep 21, 2023
1 parent 3e26542 commit 7f8b9df
Showing 1 changed file with 122 additions and 37 deletions.
159 changes: 122 additions & 37 deletions articles/write/deep_clone.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,53 +5,138 @@ author: lvzl

## 深拷贝
```js
function clone(dest, source, map = new WeakMap()) {
if (!isObjectOrArray(dest) || !isObjectOrArray(source)) {
return dest
}
if (map.get(source)) {
return source
}
map.set(source, true)
for (const key in source) {
if(isObject(source[key])) {
dest[key] = clone({}, source[key], map)
} else if(Array.isArray(source[key])) {
dest[key] = clone([], source[key], map)
} else {
dest[key] = source[key]
}
/**
* clone 要区分很多种类型
* Undefined、Null、Boolean、Number、String、Array、Object、Function、Map、Set、WeakMap、WeakSet、Symbol、BigInt、Date、Regexp、Arguments
* 可枚举类型:Object Array Map Set Arguments
* 可直接赋值:'undefined', 'string','number','boolean','symbol','bigint'
* 要特殊处理的类型 function Error
* 这些用法需要特殊处理:new Number(2)、new String('')、new RegExp(//)、new Boolean(false)、new Date()、new Error()
*/

// while 循环更快
const foreach = (keys, cb) => {
let i = 0
while(i < keys.length) {
cb(keys[i])
i++
}
return dest
}
// 可继续遍历
const mapTag = '[object Map]'
const setTag = '[object Set]'
const arrayTag = '[object Array]'
const objectTag = '[object Object]'
const arguTag = '[object Arguments]'

const canFor = [ mapTag, setTag, arrayTag, objectTag, arguTag ]

function isObjectOrArray(obj) {
const type = Object.prototype.toString.call(obj)
return type === '[object Object]' || type === '[object Array]'
// 特殊
const funcTag = '[object Function]'
const regExpTag = '[object RegExp]'
const errorTag = Error

const getType = (val) => {
return Object.prototype.toString.call(val)
}

function isObject(obj) {
return Object.prototype.toString.call(obj) === '[object Object]'
const isNormalType = val => {
if (val === null) {
return true
}
const typ = typeof val
return ['undefined', 'string','number','boolean','symbol','bigint'].includes(typ)
}
/**
* 复制一个函数
* @param {} fn
*/
const cloneFunction = (fn) => {
const fnStr = fn.toLocaleString()
if (/((function\s+)\w*\s*\()|^\(/.test(fnStr)) {
return eval('(() => (' + fnStr + '))()')
}
return eval('(() => (function ' + fnStr + '))()')
}

function isArray(obj) {
return Array.isArray(obj)
/**
* 比如 new Number(2)、new String('')、new RegExp()、new Boolean(false)、new Date()、new Error()
* @param {*} fn
*/
const cloneObjectCanNotFor = (val) => {
const ctor = val.constructor
let value = val.valueOf()
if (ctor === errorTag) { // 特殊一点,获取到的信息替换一下 Error:
value = value.message
}
return new ctor(value)
}

// test
const source = {
a: 1,
b: 2,
c: null,
d: undefined,
e: '',
f: [1,2,3],
g: {
h: 20,
i: {j: '123', k: [{l: 888}]}
function deepClone(val, map = new WeakMap()) {
// 非对象类型的: string、number、boolean、symbol、bigint、null false undefined,这些可直接赋值
if (isNormalType(val)) {
return val
}
// 接下来都是 typeof 为 object ,先获取具体的类型
const typ = getType(val)

if (map.has(val)) {
return map.get(val)
}

// function
if(typ === funcTag) {
return cloneFunction(val)
}

let res

// 可枚举的
if(canFor.includes(typ)) {
if(typ === objectTag || typ === arrayTag || typ === arguTag) {
res = Array.isArray(val) ? [] : {}
const keys = Object.keys(val)
foreach(keys, key => {
res[key] = deepClone(val[key], map)
})
} else if (typ === setTag) {
res = new Set()
val.forEach(s => {
res.add(deepClone(s))
})
} else if (typ === mapTag) {
res = new Map()
val.forEach(mapkey => {
res.set(mapkey, deepClone(val.get(mapkey)))
})
}
map.set(val, res)
} else {
return cloneObjectCanNotFor(val)
}
return res
}

// Undefined、Null、Boolean、Number、String、Array、Object、Function、Map、Set、WeakMap、WeakSet、Symbol、BigInt、Date、Regexp、Arguments
const obj = {
a: null,
b: undefined,
c: [1,2,3],
d: false,
e: true,
f: 33444,
g: '46576',
i: {aa: '', bb: 3},
j() {},
h: new Map().set({}, 2),
k: new Set().add(344, 'ee'),
l: Symbol('d'),
m: BigInt(12346575),
n: new Date('2023-01-01'),
o: new Error('haha'),
p: () => { console.log('jiantouhanshu') },
r: new RegExp('function')
}
source.s = source

const aaa = clone({}, source)
const f = deepClone(obj)
```

0 comments on commit 7f8b9df

Please sign in to comment.