如何正确克隆一个JavaScript对象

如何正确克隆一个JavaScript对象

技术背景

在JavaScript里,对象属于引用类型。这意味着直接用赋值操作符(=)来复制对象时,实际上只是复制了对象的引用,而不是对象本身。所以,对新变量进行修改会影响到原对象。因此,我们常常需要克隆对象来获得一个独立的副本。

实现步骤

1. 使用结构化克隆(Structured Cloning)

这是一个新的JS标准,在很多浏览器都能使用。

1
const clone = structuredClone(object);

2. 递归复制

1
2
3
4
5
6
7
8
function clone(obj) {
if (null == obj || "object" != typeof obj) return obj;
var copy = obj.constructor();
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
}
return copy;
}

3. 处理特定类型的递归复制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function clone(obj) {
var copy;

// 处理3种简单类型,以及null或undefined
if (null == obj || "object" != typeof obj) return obj;

// 处理Date
if (obj instanceof Date) {
copy = new Date();
copy.setTime(obj.getTime());
return copy;
}

// 处理Array
if (obj instanceof Array) {
copy = [];
for (var i = 0, len = obj.length; i < len; i++) {
copy[i] = clone(obj[i]);
}
return copy;
}

// 处理Object
if (obj instanceof Object) {
copy = {};
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
}
return copy;
}

throw new Error("Unable to copy obj! Its type isn't supported.");
}

4. 使用JSON方法

1
const clone = JSON.parse(JSON.stringify(object));

5. 使用Object.assign()

1
var clone = Object.assign({}, obj);

6. 使用扩展运算符(Spread Operator)

1
let cloned = {...obj};

7. 使用jQuery

1
2
3
4
// 浅拷贝
var copiedObject = jQuery.extend({}, originalObject);
// 深拷贝
var copiedObject = jQuery.extend(true, {}, originalObject);

8. 使用Lodash

1
var y = _.clone(x, true);

核心代码

递归复制函数

1
2
3
4
5
6
7
8
function clone(obj) {
if (null == obj || "object" != typeof obj) return obj;
var copy = obj.constructor();
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
}
return copy;
}

处理特定类型的递归复制函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function clone(obj) {
var copy;

if (null == obj || "object" != typeof obj) return obj;

if (obj instanceof Date) {
copy = new Date();
copy.setTime(obj.getTime());
return copy;
}

if (obj instanceof Array) {
copy = [];
for (var i = 0, len = obj.length; i < len; i++) {
copy[i] = clone(obj[i]);
}
return copy;
}

if (obj instanceof Object) {
copy = {};
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
}
return copy;
}

throw new Error("Unable to copy obj! Its type isn't supported.");
}

最佳实践

  • 浅拷贝场景:如果对象没有嵌套对象,使用Object.assign()或者扩展运算符(...)会比较方便。
1
2
let obj = {a: 1, b: 2};
let cloned = {...obj};
  • 深拷贝场景:若对象有嵌套对象,推荐使用递归复制函数或者JSON.parse(JSON.stringify())。不过JSON.parse(JSON.stringify())不能处理函数、undefinedRegExp等特殊类型。
1
2
let obj = {a: {b: 1}};
let cloned = clone(obj);
  • 使用第三方库:若需要处理复杂的克隆需求,可考虑使用Lodash或者jQuery。
1
var y = _.clone(x, true);

常见问题

1. 循环引用问题

使用递归复制函数或者JSON.parse(JSON.stringify())时,若对象存在循环引用,会导致栈溢出错误。可以使用带有缓存的递归复制函数来解决此问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function cloneDR(o) {
const gdcc = "__getDeepCircularCopy__";
if (o !== Object(o)) {
return o; // 原始值
}

var set = gdcc in o,
cache = o[gdcc],
result;
if (set && typeof cache == "function") {
return cache();
}
// 否则
o[gdcc] = function() { return result; }; // 覆盖
if (o instanceof Array) {
result = [];
for (var i = 0; i < o.length; i++) {
result[i] = cloneDR(o[i]);
}
} else {
result = {};
for (var prop in o)
if (prop != gdcc)
result[prop] = cloneDR(o[prop]);
else if (set)
result[prop] = cloneDR(cache);
}
if (set) {
o[gdcc] = cache; // 重置
} else {
delete o[gdcc]; // 再次取消设置
}
return result;
}

2. 特殊类型处理问题

JSON.parse(JSON.stringify())不能处理函数、undefinedRegExp等特殊类型。对于这些特殊类型,需要使用其他方法进行处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function clone(obj) {
if (null == obj || "object" != typeof obj) return obj;
if (obj instanceof Date) {
return new Date(obj.getTime());
}
if (obj instanceof RegExp) {
return new RegExp(obj);
}
if (typeof obj === 'function') {
return obj.bind({});
}
var copy = obj.constructor();
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
}
return copy;
}

如何正确克隆一个JavaScript对象
https://119291.xyz/posts/2025-05-09.how-to-correctly-clone-a-javascript-object/
作者
ww
发布于
2025年5月9日
许可协议