JS深拷贝代码实现(包含Map Set等)

56 阅读2分钟
文章开头第一句加入:本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

一、JS数据类型

JS数据类型分为两大类:原始值类型和引用类型 其中,原始值类型的数据值直接存储在栈中,有七大类。无所谓深拷贝浅拷贝。 引用类型因为数据相对复杂,其真实数据存储在堆中,然后将其引用地址存储在栈中。深拷贝浅拷贝都是针对引用类型数据。

二、赋值和拷贝定义

三种定义: 赋值:针对原始值类型, 浅拷贝:仅拷贝了栈中的引用地址,共享堆中的一份数据。修改会同时受到影响。 深拷贝:深度完全拷贝堆中的数据,分别在堆中存储相互独立的两份数据,各自引用地址分别指向各自独立的数据,互不影响。

三、具体实现

需要针对JS的两种数据类型,分别处理:

  1. 基本数据类型:不做处理,直接返回 - string, number, boolean, null, undefined, symbol, BigInt
  2. 引用数据类型:分别针对不同对象类型分别不同处理方式,其中Array和普通Object,Map, Set,WeakMap, WeakSet递归拷贝
    • Function
    • RegExp
    • Date
    • Object
    • Array
    • Map
    • Set
    • Weakmap
    • WeakSet
const deepClone=(data)=>{
    // 1. 原始值类型:不需要做处理,直接返回原始值即可
    if(isPrimitives(data)) return data; 

    // 2. 引用类型:针对不同引用数据类型,做不同处理
    const type=getDataType(data);
    console.log(type)
    if(type ==='Function'){
        return data.bind(this,arguments);
    }
    if(type ==='Date'){
        return new Date(date);
    }
    if(type ==='RegExp'){
        return new RegExp(data.source)
    }
    if(type ==='Object'){
        let result={};
        for(let key in data){
            if(data.hasOwnProperty(key)){
                result[key]=deepClone(data[key]);
            }
        }
        return result;
    }
    if(type ==='Array'){
        return data.map(item => deepClone(item));
    }
    if(type ==='Set'||type==='WeakSet'){
        let result=new data.__proto__.constructor();
        for(let item of data){
            result.add(deepClone(item));
        }
        return result;
    }
    if(type==='Map'||type==='WeakMap'){
        // Both key or value can be any kind of data
        let result=new data.__proto__.constructor();
        for(let item of data){
            result.set(deepClone(item[0]),deepClone(item[1]))
        }
        return result;
    }
}

const isPrimitives=(data)=>{
    // 1. Using typeof
    const type=typeof data;
    return type===null || (type !=='object' && type !=='function');

    // 2. Using toString()
    // const list= ['Null','Undefined', 'Number', 'String', 'Symbol', 'BigInt'];
    // return list.includes(getDataType(data));
}

const getDataType=(data)=>{
    return Object.prototype.toString.call(data).slice(8,-1);
}

/** Test */
let arr=[1,[3],5]
let arr1=deepClone(arr);
arr1[1]=2
console.log(arr,arr1);

let obj={'a':'a','b':{'bb':'bb'}}
let obj1=deepClone(obj)
obj1.b.bb='cc'
console.log(obj,obj1);

let date=new Date(628021800000)
let date1=deepClone(date)
let date2=date1; // 浅拷贝
date1.setYear(90)
console.log(date,date1,date2)

let map=new Map();
map.set('a','a')
let keyArr=[1,2];
map.set(keyArr,'b')
let map1=deepClone(map);
map1.set('a','aa')
map1.set(keyArr,'bb')
console.log(map,map1);

let set=new Set();
set.add('a')
let set1=deepClone(set);
set1.add('aa')
console.log(set,set1);