如何实现一个深拷贝 (cloneDeep)
更多描述
const obj = {
re: /hello/,
f() {},
date: new Date(),
map: new Map(),
list: [1, 2, 3],
a: 3,
b: 4,
};
cloneDeep(obj);
Issue 欢迎在 Gtihub Issue 中回答此问题: Issue 203 (opens in a new tab)
Author 回答者: coder-eric (opens in a new tab)
const oldJson = { a: 1 };
const newJson = JSON.parse(JSON.stringify(oldJson));
oldJson.a = 2;
console.log(oldJson); // {a: 2}
console.log(newJson); // {a: 1}
Author 回答者: miaooow (opens in a new tab)
function getType(obj){ return Object.prototype.toString.call(obj).slice(8,-1); } function cloneDeep(obj){ let target = {}; if(getType(obj)==='Object'){ for(let key in obj){ let item = obj[key]; target[key]=cloneDeep(item); } return target; }else if(getType(obj)==='Array'){ return obj.map(item => cloneDeep(item) ) }else{ return obj; } }
var obj = {foo:function(){},bar:1,name:'cat'}
var objClone = cloneDeep(obj)
Author 回答者: shfshanyue (opens in a new tab)
参考: clone (opens in a new tab)
- 如何处理复杂对象,如
Date
、Regexp
等 - 如何处理循环引用
Author 回答者: haiifeng (opens in a new tab)
const oldJson = { a: 1} const newJson = {} Object.assign(newJson, oldJson) oldJson.a = 2 console.log(oldJson) // {a: 2} console.log(newJson) // {a: 1}
对于深层的复杂类型,assign其实是浅拷贝
Author 回答者: haotie1990 (opens in a new tab)
/**
* 深拷贝关注点:
* 1. JavaScript内置对象的复制: Set、Map、Date、Regex等
* 2. 循环引用问题
* @param {*} object
* @returns
*/
function deepClone(source, memory) {
const isPrimitive = (value) => {
return /Number|Boolean|String|Null|Undefined|Symbol|Function/.test(
Object.prototype.toString.call(value),
);
};
let result = null;
memory || (memory = new WeakMap());
// 原始数据类型及函数
if (isPrimitive(source)) {
console.log("current copy is primitive", source);
result = source;
}
// 数组
else if (Array.isArray(source)) {
result = source.map((value) => deepClone(value, memory));
}
// 内置对象Date、Regex
else if (Object.prototype.toString.call(source) === "[object Date]") {
result = new Date(source);
} else if (Object.prototype.toString.call(source) === "[object Regex]") {
result = new RegExp(source);
}
// 内置对象Set、Map
else if (Object.prototype.toString.call(source) === "[object Set]") {
result = new Set();
for (const value of source) {
result.add(deepClone(value, memory));
}
} else if (Object.prototype.toString.call(source) === "[object Map]") {
result = new Map();
for (const [key, value] of source.entries()) {
result.set(key, deepClone(value, memory));
}
}
// 引用类型
else {
if (memory.has(source)) {
result = memory.get(source);
} else {
result = Object.create(null);
memory.set(source, result);
Object.keys(source).forEach((key) => {
const value = source[key];
result[key] = deepClone(value, memory);
});
}
}
return result;
}
Author 回答者: iceycc (opens in a new tab)
(function (done) {
if (!done) return;
// 如何实现一个深拷贝 (cloneDeep)
const obj = {
re: /hello/,
f() {},
date: new Date(),
map: new Map(),
set: new Set(),
list: [1, 2, 3],
a: 3,
b: 4,
h: {
name: "wby",
age: 29,
},
e: undefined,
d: null,
};
let utils = getTypes();
const newObj = cloneDeep(obj);
console.log(newObj);
console.log(obj.map === newObj.map);
function getTypes() {
let isTypes = {};
function isTyping(typing) {
return function (value) {
return Object.prototype.toString.call(value) === `[object ${typing}]`;
};
}
let types = [
"Object",
"Function",
"RegExp",
"Map",
"Set",
"Date",
"Array",
"String",
];
for (let type of types) {
isTypes[`is${type}`] = isTyping(type);
}
return isTypes;
}
function cloneDeep(obj, memory) {
let target = Object.create(null);
memory || (memory = new WeakMap());
for (let key in obj) {
let value = obj[key];
if (typeof value !== "object" || value === null) {
target[key] = value;
} else {
if (utils.isSet(value)) {
target[key] = new Set();
for (const v of value) {
target[key].add(cloneDeep(v, memory));
}
} else if (utils.isMap(value)) {
target[key] = new Map();
for (const [k, v] of value.entries()) {
target[key].set(k, cloneDeep(v, memory));
}
} else if (utils.isObject(value)) {
target[key] = cloneDeep(value);
} else {
target[key] = new Object.prototype.constructor(value);
}
}
}
return target;
}
})(1);
Author 回答者: illumi520 (opens in a new tab)
function deepCopy(obj) {
var result = Array.isArray(obj) ? [] : {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
if (typeof obj[key] === 'object') {
result[key] = deepCopy(obj[key]); //递归复制
} else {
result[key] = obj[key];
}
}
}
return result;
}
Author 回答者: jkLennon (opens in a new tab)
const oldJson = { a: 1} const newJson = {} Object.assign(newJson, oldJson) oldJson.a = 2 console.log(oldJson) // {a: 2} console.log(newJson) // {a: 1} @kucy 对于数组等引用类型的属性值,Object.assign还是浅拷贝
Author 回答者: mrcaidev (opens in a new tab)
JS有原生的深拷贝API structuredClone
:
const obj1 = { a: 1 };
const obj2 = structuredClone(obj1);
obj2.a = 2;
console.log(obj1); // { a: 1 }
console.log(obj2); // { a: 2 }
Author 回答者: QC2168 (opens in a new tab)
// 使用array from克隆
let arr = [1, 2, 3, 4, 5, 6, [{ 1: 12, 2: 24 }]];
let arr2 = Array.from(arr);
console.log(arr === arr2);
console.log(arr); // [1,2,3,4,5,6,{"1": 0,"2": 0}]
console.log(arr2); // [1,2,3,4,5,6,{"1": 0,"2": 0}]
// 解决实现不了深拷贝
const deepClone = (arr) =>
Array.isArray(arr) ? Array.from(arr, deepClone) : arr;
let arr3 = deepClone(arr);
console.log(arr3); // [1,2,3,4,5,6,{"1": 12, "2": 24}]
arr[6][0] = { 1: 00, 2: 00 };
Author 回答者: QC2168 (opens in a new tab)
// 使用扩展运算符实现深,浅拷贝
let arr = [1, 2, 3, 4, 5, 6, [{ 1: 12, 2: 24 }]];
let arr2 = [...arr];
console.log(arr === arr2);
console.log(arr); // [1,2,3,4,5,6,{"1": 0,"2": 0}]
console.log(arr2); // [1,2,3,4,5,6,{"1": 0,"2": 0}]
// 深拷贝
const deepClone = (arr) =>
arr.map((i) => (Array.isArray(i) ? deepClone(i) : i));
let arr3 = deepClone(arr);
arr[6][0] = { 1: 00, 2: 00 };
console.log(arr3); // [1,2,3,4,5,6,{"1": 12, "2": 24}]
Author 回答者: 4may-mcx (opens in a new tab)
function deepClone(target, map = new WeakMap()) {
if (typeof target !== "object") return target;
if (map.has(target)) return map.get(target); // 解决循环引用问题
const res = Array.isArray(target) ? [] : {};
map.set(target, res);
for (const key in target) {
res[key] = deepClone(target[key], map);
}
return res;
}
// 测试
const obj = {
foo: {
bar: 2,
},
list: [1, 2, 3],
};
obj.obj = obj; // 循环引用
const obj2 = deepClone(obj);
console.log(obj, obj2);
obj.list[3] = 3333;
obj.foo.bar = 3;
console.log(obj, obj2);
Author 回答者: coderWxs (opens in a new tab)
function deepClone(obj, map = new WeakMap()) {
if (obj === null) return obj
if (obj instanceof Date) return new Date(obj)
if (obj instanceof RegExp) return new RegExp(obj)
if (typeof obj !== 'object') return obj
if (map.has(obj)) return map.get(obj) //解决循环引用
const cloneobj = new obj.constructor() //生成obj的对应类型
map.set(obj, cloneobj)
Reflect.ownKeys(obj).forEach((key) => {
cloneobj[key] = deepClone(obj[key], map)
})
return cloneobj
}
> Author
回答者: [Yinzhuo19970516](https://github.com/Yinzhuo19970516)
```js
const obj = {
re: /hello/,
f() {},
date: new Date(),
map: new Map(),
list: [1,2,3],
a: 3,
b: 4
}
function deepClone(target,map= new Map()) {
if(target === null) return null
if(Object.prototype.toString.call(target) === '[object RegExp]') {
return new RegExp(target)
}
if(Object.prototype.toString.call(target) === '[object Date]') {
return new Date(target)
}
if(typeof target === 'object') {
let cloneTarget = Array.isArray(target)?[]:{}
if(map.get(target)){
return map.get(target)
}
map.set(target,cloneTarget)
for(const key in target){
cloneTarget[key] = deepClone(target[key],map)
}
return cloneTarget
} else {
return target
}
}
Author 回答者: xicYue (opens in a new tab)
Array、Set、Map、Object中都可能出现循环引用。
const obj = {
re: /hello/,
f() {},
date: new Date(),
map: new Map(),
list: [1, 2, 3],
a: 3,
b: 4,
};
const obj2 = { loop2: obj, name: "obj2" };
obj.loop = obj2;
function deepClone(source, cache = new WeakMap()) {
//原始类型或函数直接返回
if (typeof source !== "object") {
return source;
}
//加入缓存解决循环引用
if (cache.has(source)) {
return cache.get(source);
}
let res = new source.constructor();
cache.set(source, res);
//处理JS内置数据结构:Array、Map、Set、Object
if (source instanceof Array) {
source.forEach((v) => {
res.push(deepClone(v, cache));
});
} else if (source instanceof Map) {
for (const [k, v] of source) {
res.set(k, deepClone(v, cache));
}
} else if (source instanceof Set) {
for (const v of source) {
res.add(deepClone(v, cache));
}
} else if (Object.prototype.toString.call(source) == "[object Object]") {
for (const key in source) {
res[key] = deepClone(source[key], cache);
}
} else {
//处理自定义对象(需遵循协议new constructors时为深拷贝)
res = new source.constructor(source);
}
return res;
}
const newObj = deepClone(obj);
console.log(newObj);
Author 回答者: redsanjin-1 (opens in a new tab)
Array、Set、Map、Object中都可能出现循环引用。
const obj = { re: /hello/, f() {}, date: new Date(), map: new Map(), list: [1, 2, 3], a: 3, b: 4, }; const obj2 = { loop2: obj, name: "obj2" }; obj.loop = obj2; function deepClone(source, cache = new WeakMap()) { //原始类型或函数直接返回 if (typeof source !== "object") { return source; } //加入缓存解决循环引用 if (cache.has(source)) { return cache.get(source); } let res = new source.constructor(); cache.set(source, res); //处理JS内置数据结构:Array、Map、Set、Object if (source instanceof Array) { source.forEach((v) => { res.push(deepClone(v, cache)); }); } else if (source instanceof Map) { for (const [k, v] of source) { res.set(k, deepClone(v, cache)); } } else if (source instanceof Set) { for (const v of source) { res.add(deepClone(v, cache)); } } else if (Object.prototype.toString.call(source) == "[object Object]") { for (const key in source) { res[key] = deepClone(source[key], cache); } } else { //处理自定义对象(需遵循协议new constructors时为深拷贝) res = new source.constructor(source); } return res; } const newObj = deepClone(obj); console.log(newObj);
typeof null 也是返回 'object' 的,建议都用 Object.prototype.toString.call() 判断吧
Author 回答者: xicYue (opens in a new tab)
这是来自QQ邮箱的假期自动回复邮件。
您好,我最近正在休假中,无法亲自回复您的邮件。我将在假期结束后,尽快给您回复。
Author 回答者: hviwen (opens in a new tab)
const object = {
arr: [
{
b: {
c: 3,
},
},
{
b: {
c: 4,
},
d: 5,
e: [
{
f: 6,
},
{
g: 7,
h: {
i: 8,
j: [
{
k: 9,
},
{
l: 10,
m: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "10"],
},
],
},
},
],
},
],
func() {},
date: new Date(),
map: new Map(),
set: new Set(),
weakMap: new WeakMap(),
weakSet: new WeakSet(),
booleanT: true,
booleanF: false,
funcGet: (x) => {
console.log(x);
return x + 1;
},
symbol: Symbol("x"),
nul: null,
undefin: undefined,
nan: NaN,
integrity: Infinity,
number: 42,
regexp: /hello/,
bigint: BigInt("987654321"),
str: "str",
promise: new Promise((resolve, reject) => {
resolve("promise 1");
}),
div: document.createElement("div", {
id: "div",
class: "div",
}),
generator: function* generatorFunc() {
yield "yield 1";
},
err: new Error("error"),
};
function deepClone(origin, hashMap = new WeakMap()) {
if (origin == undefined || typeof origin !== "object") {
return origin;
}
if (origin instanceof Date) {
return new Date(origin.getTime());
}
if (origin instanceof RegExp) {
return new RegExp(origin);
}
if (origin instanceof HTMLElement) {
return origin.cloneNode(true);
}
if (origin instanceof Promise) {
return origin;
}
if (hashMap.has(origin)) {
return hashMap.get(origin);
}
let targetClone = new origin.constructor();
hashMap.set(origin, targetClone);
Reflect.ownKeys(origin).forEach((key) => {
targetClone[key] = deepClone(origin[key], hashMap);
});
return targetClone;
}
Author 回答者: loveminxo (opens in a new tab)
这是来自QQ邮箱的假期自动回复邮件。你好,我最近正在休假中,无法亲自回复你的邮件。我将在假期结束后,尽快给你回复。