序言
本篇文章主要是为了探索下是如何进行深度的复制的,其实小编也看过很多大佬写的deepcopy了,但是其实自己也想写一个毕竟用自己的插件还是最爽的,好了,话不多说开始吧。。。
前奏
开始之前先给大家介绍一款插件where-type,这个插件具体干什么的呢,大家可以看详细的README,其实就是以各种方式获取数据类型的,个人感觉不错。也希望大家多多关注下
直接上代码
1. 如何判断基本数据类型
// --------------- 定义部分 ----------------------
const { getTypes } = require('where-type')
// 这些对象值得深度克隆
const OBJECT_TYPES = [
{},
[],
new Map(),
new Set(),
new Error(),
new Date(),
/^$/
].map((item) => getTypes(item))
// ------------------- 代码判断部分 ----------------
// 除了上述的类型 都是按基本数据类型计算
const type = getTypes(target)
if (!OBJECT_TYPES.includes(type)) return target
- 通过上述的变量OBJECT_TYPES,我们可以判断如果遇到这些类型以外的类型,我们就认为是基本数据类型
- 但是,也许很多人会有疑问,比如函数呢,Symbol类型呢,我们都认为是基本数据类型吗
- 其实我个人认为函数是没有必须复制的,使用原来的函数就行。原因有二:
- 其一是函数一般都是直接调用了,要么就是进行逻辑处理,要么就是需要返回值
- 其二是函数的比较实在是没有意义,最起码到现在为止小编是没有见过函数用来进行比较的
- 如果有的小伙伴就是非常的固执,就是想进行比较那么可以参照下面的写法
if (isFunction(target)) return (...args) => target.call(this, ...args)
- 关于Symbol类型该做如何处理,我们会在后续慢慢讲解
- 那么接下来就是判断上面被过滤的类型
2. 判断Error 以及Date类型
// ------------ 定义类型的部分 --------------------
const { getTypes } = require('where-type')
const CONSTRUCT_TYPE = [new Error(), new Date()].map((item) => getTypes(item))
// --------------- 代码执行部分 ------------------
// 针对error,date做处理
if (CONSTRUCT_TYPE.includes(type)) return new target.constructor(target)
- 针对Error,Date类型判断,首先这些类型可以通过new来调用的,最起码说明我们可以获取到Constructor。既然能拿到Constructor,那我们自己就可以来new一下子
- 可能有一部分人不了解Constructor怎么来的,接下来我们解析下:
- 首先我们拿到的一定是实例,就拿Date类型来说的,例如实例是date。
- date的原型链指向谁呢,
date.__proto__ === Date.prototype
, 通过左侧的等式我们就可以找到原型,其实原型上就有属性Consructor
- 通过上述的推断,我们直接
new target.Constructor(target)
不就是一个新实例吗
3. 判断Symbol类型
// ----------------- 定义部分 ---------------------
const { getTypes } = require('where-type')
const SYMBOL_TYPE = getTypes(Symbol('1'))
// ----------------- 代码执行部分 -----------------
if (SYMBOL_TYPE === type) return Object(Symbol.prototype.valueOf.call(target))
- 我们通过
Symbol.prototype.valueOf
来获取Symbol的值 - 我们可以通过Object来获取新的Symbol()值
4. 判断正则表达式
// ----------------- 定义部分 -----------------------
const { getTypes } = require('where-type')
const REGEXP_TYPE = getTypes(/^$/)
// ---------------- 代码执行部分 --------------------
// 针对正则表达式
if (REGEXP_TYPE === type) {
const flags = /\w*$/
const result = new target.constructor(target.source, flags.exec(target))
result.lastIndex = target.lastIndex
return result
}
- 其实正则表达式的判断跟之前的很类似,但是值得我们注意的是一些细节:
- 其实上述的处理添加了一个备选正则,例如:正则表达式中第二个参数,表示如果传递的参数无效的话,将从备选flags中进行匹配
- 同时需要归置lastIndex,可能有的人会有疑惑,为什么需要这么做的??是因为可能你现在深度复制的正则,之前已经被匹配过了,lastIndex已经发生了变化。所以我们需要将lastIndex进行归置
5. 针对Set,Map处理
// ----------------- 代码定义的部分 --------------------
const { getTypes } = require('where-type')
const MAP_TYPE = getTypes(new Map())
const SET_TYPE = getTypes(new Set())
// ---------------- 代码执行的部分 --------------------
// 针对set处理
if (SET_TYPE === type) {
target.forEach((value) => {
copyTarget.add(deepCopy(value, weakMap))
})
return copyTarget
}
// 针对map处理
if (MAP_TYPE === type) {
target.forEach((value, key) => {
copyTarget.set(key, deepCopy(value, weakMap))
})
return copyTarget
}
- 其实Set,Map处理就简单很多了,通过上述的代码就可以看到,无非是创建一个新的构造函数来进行添加等
结尾
以上就是我们通过深度复制的原理,以及分析。如果有不同意见的小伙伴可以直接提出来。大家一起学习学习。源代码地址见下 源代码地址
自我介绍
其实说了这么多了还没进行自我介绍了。请看下面的: