0
# Equality and Comparison
1
2
Deep equality checking and comparison utilities with special handling for complex data types including dates, arrays, objects, and symbols.
3
4
## Capabilities
5
6
### Deep Equality Comparison
7
8
Functions for performing deep equality checks that handle JavaScript's complex equality semantics.
9
10
```typescript { .api }
11
/**
12
* Perform deep loose equality comparison between two values
13
* Handles special cases for dates, symbols, arrays, and objects
14
* @param a - First value to compare
15
* @param b - Second value to compare
16
* @returns True if values are loosely equal
17
*/
18
function looseEqual(a: any, b: any): boolean;
19
20
/**
21
* Find index of value in array using loose equality comparison
22
* Uses looseEqual for comparison instead of strict equality
23
* @param arr - Array to search in
24
* @param val - Value to find
25
* @returns Index of value or -1 if not found
26
*/
27
function looseIndexOf(arr: any[], val: any): number;
28
```
29
30
**Usage Examples:**
31
32
```typescript
33
import { looseEqual, looseIndexOf } from "@vue/shared";
34
35
// Basic equality (same as === for primitives)
36
console.log(looseEqual(1, 1)); // true
37
console.log(looseEqual("hello", "hello")); // true
38
console.log(looseEqual(1, "1")); // false (different types)
39
40
// Date comparison (compares timestamps)
41
const date1 = new Date("2023-01-01");
42
const date2 = new Date("2023-01-01");
43
const date3 = new Date("2023-01-02");
44
45
console.log(looseEqual(date1, date2)); // true (same timestamp)
46
console.log(looseEqual(date1, date3)); // false (different timestamps)
47
console.log(date1 === date2); // false (different objects)
48
49
// Symbol comparison (always strict)
50
const sym1 = Symbol("test");
51
const sym2 = Symbol("test");
52
const sym3 = sym1;
53
54
console.log(looseEqual(sym1, sym2)); // false (different symbols)
55
console.log(looseEqual(sym1, sym3)); // true (same symbol)
56
57
// Array comparison (recursive)
58
const arr1 = [1, 2, { a: 3 }];
59
const arr2 = [1, 2, { a: 3 }];
60
const arr3 = [1, 2, { a: 4 }];
61
62
console.log(looseEqual(arr1, arr2)); // true (deep equal)
63
console.log(looseEqual(arr1, arr3)); // false (nested object differs)
64
console.log(arr1 === arr2); // false (different array objects)
65
66
// Object comparison (recursive)
67
const obj1 = {
68
name: "John",
69
age: 30,
70
hobbies: ["reading", "coding"],
71
birth: new Date("1993-01-01")
72
};
73
const obj2 = {
74
name: "John",
75
age: 30,
76
hobbies: ["reading", "coding"],
77
birth: new Date("1993-01-01")
78
};
79
const obj3 = {
80
name: "John",
81
age: 31,
82
hobbies: ["reading", "coding"],
83
birth: new Date("1993-01-01")
84
};
85
86
console.log(looseEqual(obj1, obj2)); // true (deep equal)
87
console.log(looseEqual(obj1, obj3)); // false (age differs)
88
89
// Nested structure comparison
90
const complex1 = {
91
users: [
92
{ id: 1, profile: { name: "Alice", tags: ["admin"] } },
93
{ id: 2, profile: { name: "Bob", tags: ["user"] } }
94
],
95
settings: { theme: "dark", notifications: true }
96
};
97
98
const complex2 = {
99
users: [
100
{ id: 1, profile: { name: "Alice", tags: ["admin"] } },
101
{ id: 2, profile: { name: "Bob", tags: ["user"] } }
102
],
103
settings: { theme: "dark", notifications: true }
104
};
105
106
console.log(looseEqual(complex1, complex2)); // true
107
108
// Array indexOf with loose equality
109
const mixedArray = [
110
1,
111
"hello",
112
{ name: "test" },
113
[1, 2, 3],
114
new Date("2023-01-01")
115
];
116
117
// Find equivalent values
118
console.log(looseIndexOf(mixedArray, 1)); // 0
119
console.log(looseIndexOf(mixedArray, { name: "test" })); // 2
120
console.log(looseIndexOf(mixedArray, [1, 2, 3])); // 3
121
console.log(looseIndexOf(mixedArray, new Date("2023-01-01"))); // 4
122
123
// Not found
124
console.log(looseIndexOf(mixedArray, { name: "other" })); // -1
125
console.log(looseIndexOf(mixedArray, [1, 2, 4])); // -1
126
```
127
128
### Equality Comparison Rules
129
130
**looseEqual** follows these rules in order:
131
132
1. **Strict Equality**: If `a === b`, return `true`
133
2. **Date Objects**: Compare using `getTime()` if both are dates
134
3. **Symbols**: Use strict equality (symbols are unique)
135
4. **Arrays**: Compare length first, then recursively compare each element
136
5. **Objects**: Compare key count first, then recursively compare each property
137
6. **Fallback**: Convert both to strings and compare
138
139
### Special Case Handling
140
141
**Date Comparison:**
142
```typescript
143
const d1 = new Date("2023-01-01T00:00:00.000Z");
144
const d2 = new Date("2023-01-01T00:00:00.000Z");
145
looseEqual(d1, d2); // true - compares timestamps
146
```
147
148
**Array vs Non-Array:**
149
```typescript
150
looseEqual([1, 2], "1,2"); // false - different types
151
looseEqual([], ""); // false - different types
152
```
153
154
**Object Property Order:**
155
```typescript
156
looseEqual({ a: 1, b: 2 }, { b: 2, a: 1 }); // true - order doesn't matter
157
```
158
159
**Missing vs Undefined Properties:**
160
```typescript
161
looseEqual({ a: 1 }, { a: 1, b: undefined }); // false - different key counts
162
```
163
164
**Circular References:**
165
```typescript
166
const circular1: any = { name: "test" };
167
circular1.self = circular1;
168
169
const circular2: any = { name: "test" };
170
circular2.self = circular2;
171
172
// Will cause maximum call stack exceeded error
173
// looseEqual(circular1, circular2); // ❌ Don't do this
174
```
175
176
### Performance Characteristics
177
178
- **Early Returns**: Exits quickly for strict equality and type mismatches
179
- **Length Checks**: Compares array/object sizes before deep comparison
180
- **Recursive**: Can handle deeply nested structures (limited by call stack)
181
- **No Caching**: Each comparison is independent (no memoization)
182
183
### Common Use Cases
184
185
**Form Validation:**
186
```typescript
187
function hasFormChanged(currentValues: any, originalValues: any) {
188
return !looseEqual(currentValues, originalValues);
189
}
190
```
191
192
**Array Operations:**
193
```typescript
194
function removeByValue(arr: any[], value: any) {
195
const index = looseIndexOf(arr, value);
196
if (index > -1) {
197
arr.splice(index, 1);
198
}
199
}
200
```
201
202
**Change Detection:**
203
```typescript
204
function shouldUpdate(newProps: any, oldProps: any) {
205
return !looseEqual(newProps, oldProps);
206
}
207
```
208
209
### Limitations
210
211
- **Circular References**: Will cause stack overflow
212
- **Function Comparison**: Functions are compared by reference, not implementation
213
- **Class Instances**: Compared as plain objects (only enumerable properties)
214
- **Prototype Chain**: Only checks own properties, ignores prototype
215
- **Performance**: Deep comparison can be expensive for large nested structures