js深拷贝进阶---几种特殊情况的深拷贝

相信前端小伙伴提到浅拷贝/深拷贝都不会陌生,笔者碰到过几个新奇的深拷贝(symbol深拷贝,对象自身嵌套深拷贝,函数深拷贝)情况,刚开始觉得还挺有难度,但是弄懂之后觉得还蛮有意思,希望以下内容能帮助到大家。

如果还不了解深拷贝的小伙伴可以先学习下这篇文章:https://blog.csdn.net/weixin_51472145/article/details/125119338?spm=1001.2014.3001.5501

目录

1.symbol深拷贝

难点

思路方法

代码

2.对象自身嵌套深拷贝

难点 

思路方法

代码

3.函数深拷贝

难点

思路方法

代码


1.symbol深拷贝

难点

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]
        }
    )
}

2.对象自身嵌套深拷贝

(吐槽一下,这题是笔者之前大三找寒假实习,一家小公司的一面面试题,面试官基本上全程问的都是这种偏难怪的题,那次面完之后心态直接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
    } 
}   

3.函数深拷贝

如果我们的对象里面嵌套了函数,那么又该如何深拷贝函数呢。

笔者之前在网上搜这个问题的时候,相关文章和回答其实没多少,面试大概率也不会问到,面试如果问到了深拷贝,可以提一嘴这个来炫炫技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

你可能感兴趣的:(javascript,开发语言,ecmascript,前端,算法)