手写深拷贝
方法一,用 JSON:
const b = JSON.parse(JSON.stringify(a))
答题要点是指出这个方法有如下缺点:
- 不支持 Date、正则、undefined、函数等数据
- 不支持引用(即环状结构)
- 必须说自己还会方法二
方法二,用递归:
思路: 参数:(data,cache)
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
递归
不拷贝原型上的属性
逻辑添加环:数据唯一性
- 开头添加 cache:
if(!cache){ cache = new Map()}
- 对象
- 存在返回
if(cache.has(data)return cache.get(data))
- 递归前数据存入
cache.set(data, result)
key:value
- 存在返回
- 开头添加 cache:
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
如何实现深拷贝?
- 要点:
- 浅拷贝
- 递归
- 需要忽略原型(浪费内存)
- 判断类型
- 检查环(也叫循环引用)
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'))