实现一个深拷贝
const mapTag = '[object Map]'
const setTag = '[object Set]'
const arrayTag = '[object Array]'
const objectTag = '[object Object]'
const argsTag = '[object Arguments]'
const boolTag = '[object Boolean]'
const dateTag = '[object Date]'
const numberTag = '[object Number]'
const stringTag = '[object String]'
const symbolTag = '[object Symbol]'
const errorTag = '[object Error]'
const regexpTag = '[object RegExp]'
const funcTag = '[object Function]'
const deepTags = [
mapTag,
setTag,
arrayTag,
objectTag,
boolTag,
dateTag,
numberTag,
stringTag,
symbolTag,
errorTag,
regexpTag,
funcTag,
]
function forEach(array, iteratee) {
let index = -1
const { length } = array
while (++index < length) iteratee(array[index], index)
return array
}
function isObject(target) {
const type = typeof target
return target && (type === 'object' || type === 'function')
}
function getType(target) {
return Object.prototype.toString.call(target)
}
function getInit(target) {
const Ctor = target.constructor
return new Ctor()
}
function cloneSymbol(target) {
return Object(Symbol.prototype.valueOf.call(target))
}
function cloneReg(target) {
const reFlags = /\w*$/
const result = new target.constructor(target.source, reFlags.exec(targe))
result.lastIndex = target.lastIndex
return result
}
function cloneFunction(func) {
const bodyReg = /(?<={)(.|\n)+(?=})/m
const paramReg = /(?<=\().+(?=\)\s+{)/
const funcString = func.toString()
if (func.prototype) {
const param = paramReg.exec(funcString)
const body = bodyReg.exec(funcString)
if (body) {
if (param) {
const paramArr = param[0].split(',')
return new Function(...paramArr, body[0])
}
return new Function(body[0])
}
return null
}
return eval(funcString)
}
function cloneOtherType(target, type) {
const Ctor = target.constructor
switch (type) {
case boolTag:
case numberTag:
case stringTag:
case errorTag:
case dateTag:
return new Ctor(target)
case regexpTag:
return new cloneReg(target)
case symbolTag:
return cloneSymbol(target)
case funcTag:
return cloneFunction(target)
default:
return null
}
}
function clone(targe, map = new WeakMath()) {
// 克隆原始类型
if (!isObject(target)) return target
// 初始化
const type = getType(target)
let cloneTarget
if (deepTag.includes(type)) cloneTarget = getInit(target, type)
else return cloneOtherType(target, type)
// 防止循环引用
if (map.get(target)) return target
map.set(target, cloneTarget)
// 克隆set
if (type === setTag) {
target.forEach((value) => cloneTarget.add(key, clone(value)))
return cloneTarget
}
// 克隆map
if (type === mapTag) {
target.forEach((val, key) => cloneTarget.set(key, clone(value)))
return cloneTarget
}
// 克隆对象和数组
const keys = type === arrayTag ? undefined : Object.keys(target)
forEach(keys || target, (value, key) => {
if (keys) key = value
cloneTarget[key] = clone(target[key], map)
})
return cloneTarget
}
实现深拷贝
function deepCopy (obj, cache = []) {
if (obj === null || typeof obj !== 'object') {
return obj
}
if (Object.prototype.toString.call(obj) === '[object Date]') return new Date(obj)
if (Object.prototype.toString.call(obj) === '[object RegExp]') return new RegExp(obj)
if (Object.prototype.toString.call(obj) === '[object Error]') return new Error(obj)
const item = cache.filter(item => item.original === obj)[0]
if (item) return item.copy
let copy = Array.isArray(obj) ? [] : {}
cache.push({
original: obj,
copy
})
Object.keys(obj).forEach(key => {
copy[key] = deepCopy(obj[key], cache)
})
return copy
}
deepCopy($obj)
/*
{
a: null
b: undefined
c: {a: 1, d: {…}}
date: Fri Apr 10 2020 20:06:08 GMT+0800 (中国标准时间) {}
e: /regexp/
f: Error: Error: error at deepCopy (<anonymous>:8:74) at <anonymous>:19:21 at Array.forEach (<anonymous>) at deepCopy (<anonymous>:18:22) at <anonymous>:24:1
func: ƒ ()
symbol: Symbol()
}
*/
/**
* 1.如何深拷贝呢
* 方法一:Json.stringify
*/
let arr = [{ name: "baozhen", age: 18 }, "dog", { habit: "打游戏" }];
let testObj = {
a: "111",
b: {
c: {
y: 123,
},
d: [1, 2, 3],
},
e: "123",
};
let copyArr = JSON.parse(JSON.stringify(arr));
copyArr[0].name = "zhenzhen";
console.log(arr[0].name); //baozhen
console.log(copyArr[0].name); //zhenzhen
/** 深拷贝成功! */
/** 正文 实现一个深拷贝 */
function myDeepCopy(obj, map = new WeakMap()) {
if (typeof obj !== "object") return;
if (obj instanceof Date) {
const copyDate = new Date();
copyDate.setTime(obj.getTime());
return copyDate;
}
if (obj instanceof RegExp) {
const Constructor = obj.constructor;
return new Constructor(obj);
}
let newObj = obj instanceof Array ? [] : {};
if (map.get(obj)) {
return map.get(obj);
}
map.set(obj, newObj);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] =
obj[key] instanceof Object ? myDeepCopy(obj[key], map) : obj[key];
}
}
return newObj;
}
let testCopyObj = myDeepCopy(testObj);
testCopyObj.b.c = { yyy: "yyy" };
console.log(testCopyObj); //{ a: '111', b: { c: { yyy: 'yyy' }, d: [ 1, 2, 3 ] }, e: '123' }
console.log(testObj); //{ a: '111', b: { c: { y: 123 }, d: [ 1, 2, 3 ] }, e: '123' }
const target = {
field1: 1,
field2: undefined,
field3: {
child: "child",
},
field4: [2, 4, 8],
};
target.target = target;
let testCopyObj2 = myDeepCopy(target);
testCopyObj2.field3 = { a: 4 };
console.log(testCopyObj2);
console.log(target);
简化
function _deepCopy(obj) {
if(typeof obj !== "object") return;
if(obj instanceof Date) {
const copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
if(obj instanceof RegExp) {
const Constructor = obj.constructor;
return new Constructor(obj);
}
let newObj = obj instanceof Array? []: {};
for(let key in obj) {
if(obj.hasOwnProperty(key)) {
newObj[key] = obj[key] instanceof Object? _deepCopy(obj[key]): obj[key];
}
}
return newObj;
}
let obj = {
name: 'Maple',
info: {
age: 20,
work: '程序'
}
}
let demo = _deepCopy(obj);
console.log(demo);
//{ name: 'Maple', info: { age: 20, work: '程序' } }
常用的实现深拷贝的方法:
使用JSON.stringify
function clone(obj) {
if(obj === null || typeof obj !== 'object') {
return obj
}
// Date类型
if(obj instanceof Date) {
const copy = new Date()
copy.setTime(obj.getTime())
return copy
}
// 正则表达式
if(obj instanceof RegExp) {
const Constructor = obj.constructor
return new Constructor(obj)
}
// 如果是数组等引用数据类型
if(obj instanceof Array || obj instanceof Object) {
const copyObj = Array.isArray(obj) ? [] : {}
for(const key in obj) {
if(obj.hasOwnProperty(key)) {
copyObj[key] = clone(obj[key])
}
}
return copyObj
}
}
// https://juejin.cn/post/6844903929705136141#heading-10
// https://juejin.cn/post/7078289953699921956
function isArray(target) {
return Array.isArray(target);
}
function isObject(target) {
return target !== null && typeof target === 'object';
}
/**
* 浅拷贝
* @param {Object} origin 源对象
* @param {Object} target 目标对象
*/
function clone(origin, target) {
var target = target || {};
for (let k in origin) {
if (origin.hasOwnProperty(key)) {
target[k] = origin[k];
}
}
return target;
}
// 基础数据类型
// ============
console.log(Object.prototype.toString.call(null));
// [object Null]
console.log(Object.prototype.toString.call(undefined));
// [object Undefined]
console.log(Object.prototype.toString.call(true));
// [object Boolean]
console.log(Object.prototype.toString.call(123));
// [object Number]
console.log(Object.prototype.toString.call('String'));
// [object String]
console.log(Object.prototype.toString.call(Symbol()));
// [object Symbol]
// 需要遍历的引用类型
// ============
console.log(Object.prototype.toString.call({}));
// [object Object]
console.log(Object.prototype.toString.call([]));
// [object Array]
console.log(Object.prototype.toString.call(new Set()));
// [object Set]
console.log(Object.prototype.toString.call(new Map()));
// [object Map]
// 不需要遍历的引用类型
// =============
console.log(Object.prototype.toString.call(new Error()));
// [object Error]
console.log(Object.prototype.toString.call(new RegExp()));
// [object RegExp]
console.log(Object.prototype.toString.call(new String()));
// [object String]
console.log(Object.prototype.toString.call(new Number()));
// [object Number]
console.log(Object.prototype.toString.call(new Boolean()));
// [object Boolean]
console.log(Object.prototype.toString.call(new Date()));
// [object Date]
// console.log(Object.prototype.toString.call(window));
// [Object global]
console.log(Object.prototype.toString.call(JSON));
// [object JSON]
console.log(Object.prototype.toString.call(Math));
// [object Math]
console.log(Object.prototype.toString.call(Symbol()));
// [object Symbol]
console.log(Object.prototype.toString.call(function () {}));
// [object Function]
/**
* 判断是否是对象
* @param {*} target
* @returns
*/
function isObject(target) {
return (
target !== null &&
(typeof target === 'object' || typeof target === 'function')
);
}
/**
* 通过 toString 方法获取类型
* @param {*} target
* @returns
*/
function getType(target) {
return Object.prototype.toString.call(target);
}
/**
* 可遍历的对象,获取初始值
* @param {*} target
* @returns
*/
function getInitialValue(target) {
const Constructor = target.constructor;
return new Constructor();
}
/**
* 拷贝除基础类型、可遍历对象外的其他类型
* @param {*} target
* @param {*} type
*/
function cloneOtherType(target, type) {
const Constructor = target.constructor;
switch (type) {
case '[object Number]': // new Number()
case '[object String]': // new String()
case '[object Boolean]': // new Boolean()
case '[object Error]': // new Error()
case '[object Date]': // new Date()
case '[object Regex]': // new Regex()
return new Constructor(target);
// Symbol
case '[object Symbol]': // new Symbol()
// 拷贝 Symbol 的包装对象的时候,首先要获取到 Symbol 本身的值,然后再用 Object() 包裹后返回
return Object(Symbol.prototype.valueOf.apply(target));
// 函数
case '[object Function]':
// 因为函数本身在哪里定义并不重要,重要的是函数在哪里调用,所以函数类型可以直接返回函数本身
// return eval(target.toString());
return new Function('return ' + target.toString())();
default:
return null;
}
}
/**
* 深度拷贝
* @param {*} target 拷贝对象
* @param {*} map 记录循环引用对象
* @returns
*/
function cloneDeep(target, map = new WeakMap()) {
// 克隆原始类型
if (!isObject(target)) {
return target;
}
const type = getType(target);
let cloneTarget;
// 需要遍历的引用对象的初始值
if (
[
'[object Object]',
'[object Array]',
'[object Map]',
'[object Set]',
].includes(type)
) {
cloneTarget = getInitialValue(target);
} else {
// 不可遍历的其他对象
return cloneOtherType(target, type);
}
// 防止循环引用
if (map.get(target)) {
return map.get(target, target);
}
map.set(target, cloneTarget);
// 拷贝对象
if (type === '[object Object]') {
const keys = Object.keys(target);
keys.forEach((key) => {
cloneTarget[key] = cloneDeep(target[key], map);
});
}
// 拷贝数组
else if (type === '[object Array]') {
target.forEach((key) => {
cloneTarget[key] = cloneDeep(target[key], map);
});
}
// 拷贝 set
else if (type === '[object Map]') {
// target.forEach((value, key) => {
// cloneTarget.set(key, cloneDeep(value, map));
// });
for (let [key, value] of target) {
cloneTarget.set(key, cloneDeep(value, map));
}
}
// 拷贝 map
else if (type === '[object Set]') {
for (let item of target) {
cloneTarget.add(cloneDeep(item, map));
}
}
return cloneTarget;
}
// test
const map = new Map();
map.set('key', 'value');
map.set('Hello', 'Word');
const set = new Set();
set.add('Set1111');
set.add('Set2222');
const obj = {
// 基础数据类型
// ==========
number: 1,
string: 'string',
boolean: true,
undefined: undefined,
null: null,
bigint: BigInt(1n),
// Object
// ==============
map,
set,
bool: new Boolean(true),
num: new Number(2),
str: new String(2),
symbol: Object(Symbol(1)),
date: new Date(),
error: new Error(),
reg: /\d+/,
func1: () => {
console.log('code秘密花园');
},
func2: function (a, b) {
return a + b;
},
};
const result = cloneDeep(obj);
console.log(target);
console.log(result);