浅谈两数全等
# 两数全等
tip:内容很长,请花点耐心看完
# ==
和 ===
- 这两个运算符都是用来
判断两个数是否相等
的 ===
: 称为等同符,当两边值的类型相同时,直接比较值,若类型不相同,直接返回 false(严格)==
:称为等值符,当等号两边的类型相同时,直接比较值是否相等,若不相同,则先转化为类型相同的值,再进行比较(不严格)
# 严格的全等
这次我们要讲的是严格的全等
你也许会觉得 我们在实际中判断两个数全等直接用 ===
就可以了,确实,在我们实际工作中 大量 判断全等的都可以用这种方法解决
那我就要问了:两个对象 你怎么判断是否全等?
# 纯对象判断全等
- 对象以
key -- value
形式存储 - 只要两个对象 都存在
key
,并且值都相等才行,和key
在对象中 先后出现的顺序没有关系 - 只要 都存在
key
并且值相等,比如 如下栗子就是 两个全等的对象
let a = {
age: 18,
name: 'coderly',
}
let b = {
name: 'coderly',
age: 18,
}
// a 和 b 内的属性 只是先后顺序不一样,但是两个数中都存在,并且值都相等 // 所以 a 应该 和 b 判 全等
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 1. 转字符串判全等
- 由前一个分析,我们知道: 只要 都存在
key
并且值相等,和key
在对象中 先后出现的顺序没有关系 - 我们无法 确定两个 对象 属性 出现的顺序都是一样的
- 所以第一步,我们应该给对象的属性进行一个排序,将它们按相同的排序方法排序
- 然后将他们转换成字符串
- 最后将转换后的 字符串 直接用
===
判断是否全等
代码:
// 根据对象 属性名 对属性进行排序
// 省略 长度比较(这个比较简单)
function objSort(data) {
var arr = []
for (var key in data) {
arr.push(key)
}
arr = arr.sort()
var newData = {}
for (var i in arr) {
var itemKey = arr[i]
newData[itemKey] = data[itemKey]
}
return newData
}
let a = {
age: 18,
name: 'coderly',
}
let b = {
name: 'coderly',
age: 18,
}
console.log('a', objSort(a))
console.log('b', objSort(b))
console.log(
'a is equal b :',
JSON.stringify(objSort(a)) === JSON.stringify(objSort(b))
)
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
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
最后输出结果:
a
和b
属性名已经有序了

- 接着将 这两个数用
JSON.stringify()
转换成 字符串,对转换后的结果进行===
判断是否全等就可以
# 2. 取出第一个数的属性,判断第二个数中有没有并且值是否全等
- 首先,我们应该考虑两个对象的属性长度是否相等,如果相等 继续比较,不相等 直接
false
- 选定一个数进行遍历,取出属性,然后判断该属性在第二个数中存不存在,不存在直接
false
,存在判断值是否相等,不相等直接false
- 最后只要出现了一个
false
,则代表两个数不全等,否则代表两个数全等
# 纯数组判断全等
- 对于一个数组,存在下标和值,并且下标一般是连续的数字,另外特殊情况下可以是字符串
- 因为数组下标是有序的,所以如果数组值位置改变,就不能是全等
比如:
let a = [1, 2, 3]
let b = [2, 1, 3]
// a 和 b 就不应该全等
1
2
3
2
3
# 转字符串判全等
- 对于数组,下标一般是数字,如果我们有数组下标既有数字又有字符串,那么会发生什么
let arr = []
arr.push('s1')
arr.push('s2')
arr['0'] = 's3'
arr['1'] = 's4'
arr['5'] = 's5'
console.log(arr)
for (let key in arr) {
console.log(key, arr[key])
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
输出结果如下:

- 我们首先定义了一个空数组
push
了两个数,当用字符串下标的形式给它赋值时,改变了push
进去的值- 所以可以得出结论:通过
arr[0]
和arr['0']
获取到和改变的是同一个内容 - 而数组中空三个位置添加值,数组的长度变成了
6
- 所以对于数组,数组长度应相等,下标对象的值也应该相等
- 首先判断数组长度,不相等 直接
false
- 而后我们将数组以
key=value
的形式连接成字符串,并且以逗号分隔 - 最后比较得到的字符串,直接用
===
判断是否全等
代码:
// 省略 长度比较(这个比较简单)
function connectString(arr) {
// 大量拼接字符串开销很大,这里用 中间数组 代替
let tmpArr = []
for (let key in arr) {
tmpArr.push(key + '=' + arr[key])
}
return tmpArr.join()
}
let arr1 = []
arr1.push('s1')
arr1.push('s2')
arr1['0'] = 's3'
arr1['1'] = 's4'
arr1['5'] = 's5'
let arr2 = []
arr2.push('s1')
arr2.push('s2')
arr2['0'] = 's3'
arr2['1'] = 's4'
arr2['5'] = 's5'
console.log('arr1_connectString:', connectString(arr1))
console.log('arr2_connectString:', connectString(arr2))
console.log('arr1 is equal arr2:', connectString(arr1) === connectString(arr2))
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
运行结果:

# 2. 取出第一个数的属性,判断第二个数中有没有并且值是否全等
- 首先,我们应该考虑两个数组的长度是否相等,如果相等 继续比较,不相等 直接
false
- 选定一个数进行遍历,取出属性,然后判断该属性在第二个数中存不存在,不存在直接
false
,存在判断值是否相等,不相等直接false
- 最后只要出现了一个
false
,则代表两个数不全等,否则代表两个数全等
# 复杂对象和复杂数组判断全等
- 上面的介绍的都是简单的对象和数组,下面我们考虑复杂的对象和数组如何判断全等
- 那么什么是复杂的对象和数组呢?
- 比如下面这种,或者比它还复杂的
let person = {
name: 'coderly',
age: 18,
sex: true,
favourites: [
'糖',
{
sports: ['羽毛球', '走路'],
},
],
address: undefined,
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 代码实现
【思路】
- 对于这样的两个数,我们没办法通过转成字符串比较
- 我们需要分情况递归比较
- 对于简单类型的数据,直接用
===
来判断是否全等 - 对于对象和数组,首先判断属性长度是否相等,不等直接
false
否则取出其中一个数进行遍历,判断遍历的值类型是否需要递归,如果不用判断另一个数中是否存在该属性,以及值是否相等 - 只要找到一个判断全等为
false
的,则两数不全等,否则两数全等
代码
function isArray(arr) {
return Array.isArray(arr)
}
function isObject(obj) {
return Object.prototype.toString.call(obj).slice(8, -1) === 'Object'
}
function isEqual(compareA, compareB) {
var result = true
var follow = async function(compareA, compareB) {
let A = compareA,
B = compareB
if (!isArray(A) && !isArray(B) && !isObject(A) && !isObject(B)) {
return (result = A === B ? result : false)
} else if (isArray(A) && isArray(B)) {
if (A.length !== B.length) {
return (result = false)
}
for (let key in A) {
// if (A[key] && B[key]) { // 无法判断 Boolean 类型的数据
if (A.hasOwnProperty(key) && B.hasOwnProperty(key)) {
if (
(isArray(A[key]) && isArray(B[key])) ||
(isObject(A[key]) && isObject(B[key]))
) {
return follow(A[key], B[key])
}
if (A[key] === B[key]) {
continue
}
return (result = false)
}
return (result = false)
}
return
}
if (isObject(A) && isObject(B)) {
if (Object.keys(A).length !== Object.keys(B).length) {
return (result = false)
}
for (let obj in A) {
// if (A[obj] && B[obj]) { // 无法判断 Boolean 类型的数据
if (A.hasOwnProperty(obj) && B.hasOwnProperty(obj)) {
if (
(isArray(A[obj]) && isArray(B[obj])) ||
(isObject(A[obj]) && isObject(B[obj]))
) {
return follow(A[obj], B[obj])
}
if (A[obj] === B[obj]) {
continue
}
return (result = false)
}
return (result = false)
}
return
}
return (result = false)
}
follow(compareA, compareB)
return result
}
let a = [
1,
2,
3,
{
a: 'a',
b: 'b',
c: [
'1',
2,
'3',
{
st: undefined,
bt: true,
},
],
},
]
let b = [
1,
2,
3,
{
b: 'b',
a: 'a',
c: [
'1',
2,
'3',
{
st: undefined,
bt: true,
},
],
},
]
console.log(isEqual(a, b))
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
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
结果
# 扩展
- 我们如果查看
vue
源码,在 工具里面可以看到判断两数全等的代码 - 代码如下:
function isObject(obj) {
return obj !== null && typeof obj === 'object'
}
/**
* Check if two values are loosely equal - that is,
* if they are plain objects, do they have the same shape?
*/
function looseEqual(a, b) {
if (a === b) return true
const isObjectA = isObject(a)
const isObjectB = isObject(b)
if (isObjectA && isObjectB) {
try {
const isArrayA = Array.isArray(a)
const isArrayB = Array.isArray(b)
if (isArrayA && isArrayB) {
return (
a.length === b.length &&
a.every((e, i) => {
return looseEqual(e, b[i])
})
)
} else if (a instanceof Date && b instanceof Date) {
return a.getTime() === b.getTime()
} else if (!isArrayA && !isArrayB) {
const keysA = Object.keys(a)
const keysB = Object.keys(b)
return (
keysA.length === keysB.length &&
keysA.every((key) => {
return looseEqual(a[key], b[key])
})
)
} else {
/* istanbul ignore next */
return false
}
} catch (e) {
/* istanbul ignore next */
return false
}
} else if (!isObjectA && !isObjectB) {
return String(a) === String(b)
} else {
return false
}
}
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
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
【分析】
- 首先通过
===
判断 全等直接return true
否则进入下一步 - 判断两个数是否为对象类型,如果都是对象类型,判断是否时数组,如果是,取里面的值一个一个比较并递归;如果不是,判断是否是
Date
类型,如果是转换成时间戳比较;否则,如果不是数组也不是Date
类型,取出里面的键-值逐个判断 - 如果两个数不是对象类型,转换为字符串比较
- 否则
return false
上次更新: 2021/08/13, 10:20:29