技术分享
首页
  • JavaScript

    • 构造函数和原型
    • Cookie和Session
    • Object.create(null)和{}
    • TypeScript配置
    • typescript入门到进阶
  • 框架

    • Vue-Router
    • React基础入门
  • 其它

    • Http协议
    • 跨域问题总结
  • 分析Promise实现
  • Axios源码分析
  • Webpack原理
  • vueRouter源码分析
  • Vue

    • Vite快速搭建Vue3+TypeScript项目
    • Cordova打包Vue项目的问题
    • Vue将汉字转为拼音,取出首字母
    • h5项目问题总结
  • JavaScript

    • new Function
  • 后端

    • Node.js中使用Crypto生成Token
    • Body-Parser处理多层对象的问题
  • 其它

    • 项目Demo汇总
    • Vuepress+Vercel搭建个人站点
    • 项目中能用到的
    • husky规范代码提交
  • Mongoose基础
  • Multer文件上传中间件的使用
  • JavaScript

    • 浅谈两数全等
    • JavaScript进制转换
    • 手写bind,apply,call和new
  • 算法

    • 数组去重和排序
    • 数组扁平化
    • 斐波那契数列
  • JavaScript 数据结构
  • 其它

    • webpack面试题
    • vite面试题
    • svg和canvas的优缺点
    • TypeScript面试题
    • Vue常见面试题
  • 计算机网络

    • 数据链路层
    • 网络层
  • Git的使用
  • Nginx的使用
  • CentOS7安装Nginx
  • 正则表达式
  • SEO搜索引擎优化
  • Serverless介绍
友链
GitHub (opens new window)

刘誉

总有人要赢,为什么不能是我
首页
  • JavaScript

    • 构造函数和原型
    • Cookie和Session
    • Object.create(null)和{}
    • TypeScript配置
    • typescript入门到进阶
  • 框架

    • Vue-Router
    • React基础入门
  • 其它

    • Http协议
    • 跨域问题总结
  • 分析Promise实现
  • Axios源码分析
  • Webpack原理
  • vueRouter源码分析
  • Vue

    • Vite快速搭建Vue3+TypeScript项目
    • Cordova打包Vue项目的问题
    • Vue将汉字转为拼音,取出首字母
    • h5项目问题总结
  • JavaScript

    • new Function
  • 后端

    • Node.js中使用Crypto生成Token
    • Body-Parser处理多层对象的问题
  • 其它

    • 项目Demo汇总
    • Vuepress+Vercel搭建个人站点
    • 项目中能用到的
    • husky规范代码提交
  • Mongoose基础
  • Multer文件上传中间件的使用
  • JavaScript

    • 浅谈两数全等
    • JavaScript进制转换
    • 手写bind,apply,call和new
  • 算法

    • 数组去重和排序
    • 数组扁平化
    • 斐波那契数列
  • JavaScript 数据结构
  • 其它

    • webpack面试题
    • vite面试题
    • svg和canvas的优缺点
    • TypeScript面试题
    • Vue常见面试题
  • 计算机网络

    • 数据链路层
    • 网络层
  • Git的使用
  • Nginx的使用
  • CentOS7安装Nginx
  • 正则表达式
  • SEO搜索引擎优化
  • Serverless介绍
友链
GitHub (opens new window)
  • 浅谈两数全等
    • == 和 ===
    • 严格的全等
      • 纯对象判断全等
      • 纯数组判断全等
      • 复杂对象和复杂数组判断全等
    • 扩展
  • JavaScript 进制转换
  • 数组去重和排序
  • 数组扁平化
  • 斐波那契数列
  • webpack面试题
  • 手写bind,apply,call和new
  • vite面试题
  • svg和canvas的优缺点
  • TypeScript面试题
  • Vue常见面试题
  • JavaScript 数据结构
  • 面试
coderly
2020-06-13

浅谈两数全等

# 两数全等

tip:内容很长,请花点耐心看完

# == 和 ===

  • 这两个运算符都是用来 判断两个数是否相等的
  • === : 称为等同符,当两边值的类型相同时,直接比较值,若类型不相同,直接返回 false(严格)
  • == :称为等值符,当等号两边的类型相同时,直接比较值是否相等,若不相同,则先转化为类型相同的值,再进行比较(不严格)

# 严格的全等

这次我们要讲的是严格的全等

你也许会觉得 我们在实际中判断两个数全等直接用 === 就可以了,确实,在我们实际工作中 大量 判断全等的都可以用这种方法解决

那我就要问了:两个对象 你怎么判断是否全等?

# 纯对象判断全等

  1. 对象以 key -- value形式存储
  2. 只要两个对象 都存在 key,并且值都相等才行,和 key 在对象中 先后出现的顺序没有关系
  3. 只要 都存在 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

# 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

最后输出结果:

  • a 和 b 属性名已经有序了
两数全等-图片
  • 接着将 这两个数用 JSON.stringify() 转换成 字符串,对转换后的结果进行 === 判断是否全等就可以

# 2. 取出第一个数的属性,判断第二个数中有没有并且值是否全等

  • 首先,我们应该考虑两个对象的属性长度是否相等,如果相等 继续比较,不相等 直接 false
  • 选定一个数进行遍历,取出属性,然后判断该属性在第二个数中存不存在,不存在直接 false,存在判断值是否相等,不相等直接 false
  • 最后只要出现了一个false,则代表两个数不全等,否则代表两个数全等

# 纯数组判断全等

  • 对于一个数组,存在下标和值,并且下标一般是连续的数字,另外特殊情况下可以是字符串
  • 因为数组下标是有序的,所以如果数组值位置改变,就不能是全等
    比如:
let a = [1, 2, 3]
let b = [2, 1, 3]
// a 和 b 就不应该全等
1
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

输出结果如下:

两数全等-图片
  1. 我们首先定义了一个空数组
  2. push了两个数,当用字符串下标的形式给它赋值时,改变了push进去的值
  3. 所以可以得出结论:通过arr[0] 和 arr['0'] 获取到和改变的是同一个内容
  4. 而数组中空三个位置添加值,数组的长度变成了 6
  5. 所以对于数组,数组长度应相等,下标对象的值也应该相等
  • 首先判断数组长度,不相等 直接 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. 取出第一个数的属性,判断第二个数中有没有并且值是否全等

  • 首先,我们应该考虑两个数组的长度是否相等,如果相等 继续比较,不相等 直接 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

# 代码实现

【思路】

  • 对于这样的两个数,我们没办法通过转成字符串比较
  • 我们需要分情况递归比较
  • 对于简单类型的数据,直接用 === 来判断是否全等
  • 对于对象和数组,首先判断属性长度是否相等,不等直接 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

结果
两数全等-图片

# 扩展

  • 我们如果查看 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

【分析】

  • 首先通过 === 判断 全等直接 return true 否则进入下一步
  • 判断两个数是否为对象类型,如果都是对象类型,判断是否时数组,如果是,取里面的值一个一个比较并递归;如果不是,判断是否是 Date 类型,如果是转换成时间戳比较;否则,如果不是数组也不是 Date 类型,取出里面的键-值逐个判断
  • 如果两个数不是对象类型,转换为字符串比较
  • 否则 return false
#JavaScript
上次更新: 2021/08/13, 10:20:29
JavaScript 进制转换

JavaScript 进制转换→

最近更新
01
代码片段
04-22
02
koa全家桶
03-29
03
mocks项目复盘
03-29
更多文章>
Theme by Vdoing | Copyright © 2021-2022 coderly | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式