-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathindex.js
121 lines (100 loc) · 2.54 KB
/
index.js
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import type from '@jkroso/type'
// (any, any, [array]) -> boolean
function equal(a, b, memos){
// All identical values are equivalent
if (a === b) return true
const fnA = types[type(a)]
const fnB = types[type(b)]
return fnA && fnA === fnB
? fnA(a, b, memos)
: false
}
const types = {}
// (Number) -> boolean
types.number = function(a, b){
return a !== a && b !== b/*Nan check*/
}
// (function, function, array) -> boolean
types['function'] = function(a, b, memos){
return a.toString() === b.toString()
// Functions can act as objects
&& types.object(a, b, memos)
&& equal(a.prototype, b.prototype)
}
// (date, date) -> boolean
types.date = function(a, b){
return +a === +b
}
// (regexp, regexp) -> boolean
types.regexp = function(a, b){
return a.toString() === b.toString()
}
// (DOMElement, DOMElement) -> boolean
types.element = function(a, b){
return a.outerHTML === b.outerHTML
}
// (textnode, textnode) -> boolean
types.textnode = function(a, b){
return a.textContent === b.textContent
}
// decorate `fn` to prevent it re-checking objects
// (function) -> function
function memoGaurd(fn){
return function(a, b, memos){
if (!memos) return fn(a, b, [])
var i = memos.length, memo
while (memo = memos[--i]) {
if (memo[0] === a && memo[1] === b) return true
}
return fn(a, b, memos)
}
}
types['arguments'] =
types['bit-array'] =
types.array = memoGaurd(arrayEqual)
// (array, array, array) -> boolean
function arrayEqual(a, b, memos){
var i = a.length
if (i !== b.length) return false
memos.push([a, b])
while (i--) {
if (!equal(a[i], b[i], memos)) return false
}
return true
}
types.object = memoGaurd(objectEqual)
// (object, object, array) -> boolean
function objectEqual(a, b, memos) {
if (typeof a.equal == 'function') {
memos.push([a, b])
return a.equal(b, memos)
}
var ka = getEnumerableProperties(a)
var kb = getEnumerableProperties(b)
var i = ka.length
// same number of properties
if (i !== kb.length) return false
// although not necessarily the same order
ka.sort()
kb.sort()
// cheap key test
while (i--) if (ka[i] !== kb[i]) return false
// remember
memos.push([a, b])
// iterate again this time doing a thorough check
i = ka.length
while (i--) {
var key = ka[i]
if (!equal(a[key], b[key], memos)) return false
}
return true
}
// (object) -> array
const getEnumerableProperties = (object) => {
const result = []
for (var k in object) if (k !== 'constructor') {
result.push(k)
}
return result
}
export default equal