相信前端小伙伴提到浅拷贝/深拷贝都不会陌生,笔者碰到过几个新奇的深拷贝(symbol深拷贝,对象自身嵌套深拷贝,函数深拷贝)情况,刚开始觉得还挺有难度,但是弄懂之后觉得还蛮有意思,希望以下内容能帮助到大家。
如果还不了解深拷贝的小伙伴可以先学习下这篇文章:https://blog.csdn.net/weixin_51472145/article/details/125119338?spm=1001.2014.3001.5501
目录
1.symbol深拷贝
难点
思路方法
代码
2.对象自身嵌套深拷贝
难点
思路方法
代码
3.函数深拷贝
难点
思路方法
代码
symbol作为对象的key时候的深拷贝,难点就是普通的遍历对象的方法访问不到symbol的键值对,所以重点在于如何访问到symbol的键值对
要想访问到对象里面的symbol,需要用到下面的方法:
// 该静态方法返回一个包含给定对象所有自有 Symbol 属性的数组。
Object.getOwnPropertySymbols()
我们来看几个例子:
const object1 = {};
const a = Symbol('a');
const b = Symbol('b');
object1[a] = 'localSymbol';
object1[b] = 'globalSymbol';
const objectSymbols = Object.getOwnPropertySymbols(object1);
console.log(objectSymbols,objectSymbols.length);
//最后输出:Array [Symbol(a), Symbol(b)] 2
有了上面的这个方法,我们接下来的代码就好说了
//下面代码只写深拷贝处理函数cloneDeep中处理symbol部分的深拷贝核心部分
//其他类型的深拷贝部分的就不写了
// 从target对象中获取symbol类型的key,并返回对象
const symKeys=Object.getOwnPropertySymbols(target)
//若数组长度不为0,遍历每一个symbol
if(symKeys.length){
symKeys.forEach(
//若target对象中该symbol key对应的value是object类型,则递归调用cloneDeep函数
if(typeof target[symKeys]=='object'&& target[symKeys]!=null{
cloneTarget[symKeys]=cloneDeep(target[symKey]);
//否则就复制到cloneTarget对象中
}else{
cloneTarget[symKeys]=target[symKeys]
}
)
}
(吐槽一下,这题是笔者之前大三找寒假实习,一家小公司的一面面试题,面试官基本上全程问的都是这种偏难怪的题,那次面完之后心态直接b溃......)
言归正传,咱们看题:
如果有个对象objA,它的结构如下,我们如何对他进行深拷贝呢?
objA={
'one':123,
'two':456,
'three':ObjA
}
普通的嵌套对象的深拷贝,我们只需要不断的递归判断对象value是不是object类型即可,如果是那就递归调用即可。简单来说就是只要value还能拆,我就继续循环调用。
而这个嵌套自身的对象,难点在于我们如果碰到的value是自身对象,那就不用再递归调用了,so我们如何实现这一功能呢?
我们需要用到JavaScript的Map数据类型(有些文章里用的是weakMap也是可以的,整体思路都一样):每碰到一个object类型的,就存到map中进行记录,根据map中是否有记录来决定是否递归调用深拷贝函数。
步骤:
step1:若碰到object类型,就先判断Map中存没存这个object
step2:若存了,那就把Map里面这个object直接返回
step2:若没存,把对应的object存到map里,之后递归调用深拷贝函数
function deepCopy(obj,hash=new Map()){
//先判断obj是不是基本类型
if(typeof obj ==object){
//先判断key为obj的value是否存在
//若存在,那么直接返回
if(hash.has(obj)) return hash.get(obj)
//判断看obj是数组还是对象
let cloneObj =Array.isArray(obj)?[]:{}
//将obj存入map中(存的value是多少不重要,重要的是map里面有obj存在的记录就行)
hash.set(obj,cloneObj)
//递归深拷贝obj
for(let key in obj){
cloneObj[key]=isObj(obj[key])?deepCopy(obj[key],hash):obj[key]
}
return cloneObj
//基本类型直接返回
}else{
return obj
}
}
如果我们的对象里面嵌套了函数,那么又该如何深拷贝函数呢。
笔者之前在网上搜这个问题的时候,相关文章和回答其实没多少,面试大概率也不会问到,面试如果问到了深拷贝,可以提一嘴这个来炫炫技hhhh。
检测出function类型其实并不难,使用instance of就行,但是把函数拷贝下来就有些难度了
思路就是检测出来function之后,先转成字符串,然后再把字符串转成函数。
如何把字符串转成函数呢,解决办法就是用Function()构造函数。
Function(arg0, arg1, /* … ,*/ argN, functionBody)
//argN(可选):被函数用作形参的名称。每一个必须是字符串,对应生成的函数的参数
//functionBody(必填):一个包含构成函数定义的 JavaScript 语句的字符串。
这里就不写具体的实现了,写个大概的核心实现逻辑:
// func就是我们想要拷贝的函数
function func(a,b,c){
return a*b*c
}
//注意后面的括号
var cloneFunc=new Function('return'+func.toString())()
解释一下:
1.func.toString()是把func转化成了字符串。
2.new Function()执行完后,会生成一个函数(我们叫他funcA吧)
funcA的返回值是func,所以我们如果想要func,就需要执行funcA,执行后才会把func给返回回来。
3.所以我们在Function()后面又加了一个括号,用来调用funcA函数,最后我们克隆完之后的函数,就赋给了cloneFunc