JavaScript中的对象比较

JavaScript中的对象比较

技术背景

在JavaScript里,直接用 ===== 比较对象时,比较的是对象的引用,而非对象内容。这就导致即使两个对象的属性和值都相同,使用这些运算符比较也会返回 false。所以,需要更复杂的方法来比较对象内容是否相同。

实现步骤

1. 快速但有局限的方法

当处理简单的JSON风格对象(无方法和DOM节点)时,可使用 JSON.stringify() 方法:

1
JSON.stringify(obj1) === JSON.stringify(obj2)

不过,这种方法对属性顺序敏感。例如:

1
2
3
x = {a: 1, b: 2};
y = {b: 2, a: 1};
console.log(JSON.stringify(x) === JSON.stringify(y)); // false

2. 慢速但更通用的方法

通过递归比较对象属性和构造函数来实现:

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
function deepCompare () {
var i, l, leftChain, rightChain;

function compare2Objects (x, y) {
var p;

if (isNaN(x) && isNaN(y) && typeof x === 'number' && typeof y === 'number') {
return true;
}

if (x === y) {
return true;
}

if ((typeof x === 'function' && typeof y === 'function') ||
(x instanceof Date && y instanceof Date) ||
(x instanceof RegExp && y instanceof RegExp) ||
(x instanceof String && y instanceof String) ||
(x instanceof Number && y instanceof Number)) {
return x.toString() === y.toString();
}

if (!(x instanceof Object && y instanceof Object)) {
return false;
}

if (x.isPrototypeOf(y) || y.isPrototypeOf(x)) {
return false;
}

if (x.constructor !== y.constructor) {
return false;
}

if (x.prototype !== y.prototype) {
return false;
}

if (leftChain.indexOf(x) > -1 || rightChain.indexOf(y) > -1) {
return false;
}

for (p in y) {
if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
return false;
}
else if (typeof y[p] !== typeof x[p]) {
return false;
}
}

for (p in x) {
if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
return false;
}
else if (typeof y[p] !== typeof x[p]) {
return false;
}

switch (typeof (x[p])) {
case 'object':
case 'function':

leftChain.push(x);
rightChain.push(y);

if (!compare2Objects (x[p], y[p])) {
return false;
}

leftChain.pop();
rightChain.pop();
break;

default:
if (x[p] !== y[p]) {
return false;
}
break;
}
}

return true;
}

if (arguments.length < 1) {
return true;
}

for (i = 1, l = arguments.length; i < l; i++) {

leftChain = [];
rightChain = [];

if (!compare2Objects(arguments[0], arguments[i])) {
return false;
}
}

return true;
}

3. ES3实现

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
function object_equals( x, y ) {
if ( x === y ) return true;

if ( ! ( x instanceof Object ) || ! ( y instanceof Object ) ) return false;

if ( x.constructor !== y.constructor ) return false;

for ( var p in x ) {
if ( ! x.hasOwnProperty( p ) ) continue;

if ( ! y.hasOwnProperty( p ) ) return false;

if ( x[ p ] === y[ p ] ) continue;

if ( typeof( x[ p ] ) !== "object" ) return false;

if ( ! object_equals( x[ p ], y[ p ] ) ) return false;
}

for ( p in y )
if ( y.hasOwnProperty( p ) && ! x.hasOwnProperty( p ) )
return false;

return true;
}

最佳实践

  • 简单对象:若对象简单且属性顺序固定,可使用 JSON.stringify() 进行快速比较。
  • 复杂对象:对于包含嵌套对象、函数和日期的复杂对象,使用递归比较函数,如 deepCompareobject_equals

常见问题

  • 属性顺序问题JSON.stringify() 对属性顺序敏感,若属性顺序不同,比较结果可能为 false
  • 函数比较:比较函数时,仅比较函数文本可能不准确,因为函数可能有不同的闭包。函数应仅在引用相同时才被视为相等。
  • 循环引用:循环引用可能导致无限递归,需在比较时进行处理。例如,在 areEquivalent 函数中使用标记属性避免无限循环。

JavaScript中的对象比较
https://119291.xyz/posts/object-comparison-in-javascript/
作者
ww
发布于
2025年6月9日
许可协议