Skip to main content

手写深拷贝

方法一,用 JSON:

const b = JSON.parse(JSON.stringify(a))

答题要点是指出这个方法有如下缺点:

  1. 不支持 Date、正则、undefined、函数等数据
  2. 不支持引用(即环状结构)
  3. 必须说自己还会方法二

方法二,用递归:

思路: 参数:(data,cache)

  1. instanceof 判断类型:

    • 对象

      • 函数 data.prototype 判断

        • 普通函数:result = function (){data.apply(this,arguments)}
        • 箭头函数:result = (...args)=>{data.call(undefined,...args)}
      • 数组:result = []

      • 日期:result = new Date(data-0)

      • 正则:result = new RegExp(data.source,data.flags)

      • 默认对象:result = {}

      • 递归(不拷贝原型上的属性)

        for(let key in data){
        if(data.hasOwnProperty(key)){
        result[key] = deepClone(data[key],cache)
        }
        }
      • 返回 result

    • 非对象:直接返回 data

  2. 递归

  3. 不拷贝原型上的属性

  4. 逻辑添加环:数据唯一性

    • 开头添加 cache:if(!cache){ cache = new Map()}
    • 对象
      • 存在返回 if(cache.has(data)return cache.get(data))
      • 递归前数据存入 cache.set(data, result) key:value
const deepClone = (data,cache) => {
// 4. 检查环:数据唯一性(防止a.self = a暴栈)
if(!cache){ cache = new Map()}

// 1. 判断类型:对象(函数(普通/箭头)/数组/日期/正则)/非对象
if(data instanceof Object){
// 存在直接返回
if(cache.has(data)){return cache.get(data)}

let result
if(data instanceof Function){ // 函数
if(data.prototype){ // 普通函数
result = function(){ return data.apply(this, arguments) }
}else{ // 箭头函数
result = (...args) => { return data.call(undefined, ...args) }
}
}else if(data instanceof Array){ // 数组
result = []
}else if(data instanceof Date){ // 日期
result = new Date(data-0)
}else if(data instanceof RegExp){ // 正则
result = new RegExp(data.source,data.flags)
}else{
result = {}
}
// 数据存入result
cache.set(data, result)
// 2. 递归,检查每一项
for (let key in data) {
// 3.不拷贝原型上的属性
if (data.hasOwnProperty(key)) {
result[key] = deepClone(data[key],cache);
}
}
return result
}else{
// 非对象
return data
}
}

使用方法:

const a = {
number: 1,
bool: false,
str: 'hi',
empty1: undefined,
empty2: null,
array: [
{ name: 'frank', age: 18 },
{ name: 'jacky', age: 19 }
],
date: new Date(2000, 0, 1, 20, 30, 0),
regex: /\.(j|t)sx/i,
obj: { name: 'frank', age: 18 },
f1: (a, b) => a + b,
f2: function (a, b) {
return a + b
}
}
a.self = a

const b = deepClone(a)
console.log('b', b)

b.self === b // true

如何实现深拷贝?

  • 要点:
    1. 浅拷贝
    2. 递归
    3. 需要忽略原型(浪费内存)
    4. 判断类型
    5. 检查环(也叫循环引用)
function clone(source, map = new Map()) {
//数据判断:排除null为object情况
if (typeof source !== 'object' || source === null) return source
//存放数据类型
let target = Array.isArray(source) ? [] : {}
//循环引用:发现相同数据,进行返回一样数据,进行循环嵌套,不存在则进行标记
if (map.get(source)) {
return map.get(source)
}
map.set(source, target)
//遍历所有属性
for (let key in source) {
//需要忽略原型(浪费内存)
if (source.hasOwnProperty(key)) {
//类型判断
if (typeof source[key] === 'object' && source[key] !== null) {
target[key] = clone(source[key], map)
} else {
target[key] = source[key]
}
}
}
return target
}

map(字典)数据结构:只能通过 map.set()设置,通过 map.get()获取

let map = new Map()
map.set('a', 1)
console.log(map.get('a'))