JavaScript判断两个值相等的方法详解

 更新时间:2022年07月11日 08:52:56   作者:颜海镜  
在 JavaScript 中如何判断两个值相等,这个问题看起来非常简单,但并非如此,在 JavaScript 中存在 4 种不同的相等逻辑,如果你不知道他们的区别,或者认为判断相等非常简单,本文就来为大家详细讲讲

前言

在 JavaScript 中如何判断两个值相等,这个问题看起来非常简单,但并非如此,在 JavaScript 中存在 4 种不同的相等逻辑,如果你不知道他们的区别,或者认为判断相等非常简单,那么本文非常适合你阅读。

ECMAScript 是 JavaScript 的语言规范,在ECMAScript 规范中存在四种相等算法,如下图所示:

上图中四种算法对应的中文名字如下,大部分前端应该熟悉严格相等和非严格相等,但对于同值零和同值却不熟悉,下面我们分别介绍这四种算法。

  • 同值
  • 同值零
  • 非严格相等
  • 严格相等

非严格相等

非严格相等使用两个等号,也就是我们熟悉的双等,非严格相等表示语义相等,不要求类型一样,非严格相等在比较前会先将比较参数类型转换为一致,再进行比较,代码示例如下:

1 == 1; // true
1 == '1'; // true 类型不同,不影响比较结果

非严格相等有非常复杂的转换规则,非常难以记忆,社区中有人将上面的规则总结成了图片,一图胜千言,如下图所示:

为了方便记住非严格相等的的转换逻辑,作者将非对象值,可以总结为如下三条规则:

  • Undefined 只和 Null 相等
  • 和 Number 比较时,另一个值会自动转换为 Number
  • 和 Boolean 比较时,另一个值会转换为 Number

如果值为对象,会使用内部的 ToPrimitive 转换,可以通过自定义 Symbol.toPrimitive 改变返回值,需要注意的是在相等的判断中 Symbol.toPrimitive 接受的 hint 参数都是 default。

const obj = {
  [Symbol.toPrimitive](hint) {
    console.log(hint);
    if (hint == 'number') {
      return 1;
    }
    if (hint == 'string') {
      return 'yan';
    }
    return true;
  },
};

console.log(obj == 1); // obj 返回 true
console.log(obj == '1'); // obj 返回 true
console.log(obj == true); // obj 返回 true

非严格相等并非带来了很多便利,通过隐式的自动转换,简化了部分场景的工作,比如 Number 和 String 的自动转换,简化了前端从表单,url 参数中获取值的比较问题,但自动转换带来的问题比便利还多。

隐式转换的规则,大部分情况下难以驾驭,现在主流的观点已经不建议使用,作者建议只在判断 undefined 和 null 的场景下可以使用非严格相等。

严格相等

严格相等是另一种比较算法,其和非严格想等的区别是不会进行类型转换,类型不一致时直接返回 false,严格相等对应===操作符,因为使用三个等号,也被称作三等或者全等,严格相等示例如下:

1 === 1; // true
1 === '1'; // false 类型不同,影响比较结果

不同类型值判断规则如下,和前面的非严格相等对比,严格相等更符合直觉。

严格相等解决了非严格相等中隐式转换带来的问题,但也丢失了隐式转换带来的便利,对于类型可能不一致的情况下,比如从表单中获取的值都是字符串,保险的做法是,在比较前手动类型转换,代码示例如下:

1 === Number('1'); // true 手动类型转换,类型防御

严格相等几乎总是正确的,但也有例外情况,比如 NaN 和正负 0 的问题。

Number 类型有个特殊的值 NaN,用来表示计算错误的情概况,比较常见是非 Number 类型和 Number 类型计算时,会得到 NaN 值,代码示例如下所示,这是从表单和接口请求获取数据时很容易出现的问题。

const a = 0 / 0; // NaN
const b = 'a' / 1;
const c = undefined + 1; // NaN

在严格相等中,NaN 是不等于自己的,NaN 是(x !== x) 成立的唯一情况,在某些场景下其实是希望能够判断 NaN 的,可以使用 isNaN 进行判断,ECMAScript 2015 引入了新的 Number.isNaN,和 isNaN 的区别是不会对传入的参数做类型转换,建议使用语义更清晰的 Number.isNaN,但是要注意兼容性问题,判断 NaN 代码示例如下:

NaN === NaN; // false

isNaN(NaN); // true
Number.isNaN(NaN); // true

isNaN('aaa'); // true 自动转换类型 'aaa'转换为Number为NaN
Number.isNaN('aaa'); // false 不进行转换,类型不为Number,直接返回false

严格相等另一个例外情况是,无法区分+0 和-0,代码示例如下,在一些数学计算场景中是要区分语义的。

+0 === -0; // true

JavaScript 中很多系统函数都使用严格相等,比如数组的 indexOf,lastIndexOf 和 switch-case 等,需要注意,这些对于 NaN 无法返回正确结果,代码示例如下:

[NaN].indexOf(NaN); // -1 数组中其实存在NaN
[NaN].lastIndexOf(NaN); // -1

同值零

同值零是另一种相等算法,名字来源于规范的直译,规范中叫做 SameValueZero,同值零和严格相等功能一样,除了处理 NaN 的方式,同值零认为 NaN 和 NaN 相等,这在判断 NaN 是否在集合中的语义下是非常合理的。

ECMAScript 2016 引入的 includes 使用此算法,此外 Map 的键去重和 Set 的值去重,使用此算法,代码示例如下:

[NaN].incdudes(NaN); // true 注意和indexOf的区别,incdudes的语义更合理

new Set([NaN, NaN]); // [NaN] set中只会有个一个NaN,如果 NaN !== NaN的话,应该是[NaN, NaN]

new Map([
  [NaN, 1],
  [NaN, 2],
]); // {NaN => 2} 如果 NaN !== NaN的话,应该是 {NaN => 1, NaN => 2}

同值

同值是最后一种相等算法,其和同值零类似,但认为 +0 不等于 -0,ECMAScript 2015 带来的 Object.is 使用同值算法,代码示例如下:

Object.is(NaN, NaN); // true
Object.is(+0, -0); // false 📢 注意这里

同值算法的使用场景是,确定两个值是否在任何情况下功能上是相同的,比较不常用,defineProperty 使用此算法确认键是否存在,例如,将存在的只读属性值-0 修改为+0 时会报错,如果设置为同样的-0 将执行正常,代码示例如下:

function test() {
  'use strict'; // 需要开启严格模式
  var a = {};

  Object.defineProperty(a, 'a1', {
    value: -0,
    writable: false,
    configurable: false,
    enumerable: false,
  });

  Object.defineProperty(a, 'a1', {
    value: -0,
  }); // 正常执行

  Object.defineProperty(a, 'a1', {
    value: 0,
  }); // Uncaught TypeError: Cannot redefine property: a1
}
test();

对于数组判断是否存在的场景,如果想区分+0 和-0,可以使用 ECMAScript 2015 引入的 find 方法,自行控制判断逻辑,代码示例如下:

[0].includes(-0); // 不能区分-0
[0].find((val) => Object.is(val, -0)); // 能区分+0和-0

总结

最后来对比下四种算法的区别,区别如下表所示:

 隐式转换NaN 和 NaN+0 和 -0
非严格相等(==)falsetrue
严格相等(===)falsetrue
同值零(includes 等)truetrue
同值(Object.is)truefalse

到此这篇关于JavaScript判断两个值相等的方法详解的文章就介绍到这了,更多相关JavaScript判断值相等内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • ionic实现底部分享功能

    ionic实现底部分享功能

    这篇文章主要为大家详细介绍了ionic实现底部分享功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-05-05
  • 微信小程序wx.request实现后台数据交互功能分析

    微信小程序wx.request实现后台数据交互功能分析

    这篇文章主要介绍了微信小程序wx.request实现后台数据交互功能,分析微信小程序wx.request在后台数据交互过程中遇到的问题与相关的解决方法,需要的朋友可以参考下
    2017-11-11
  • 原生JS实现多个小球碰撞反弹效果示例

    原生JS实现多个小球碰撞反弹效果示例

    这篇文章主要介绍了原生JS实现多个小球碰撞反弹效果,结合完整实例形式分析了javascript实现小球碰撞的相关数值计算、随机数生成、事件响应等操作技巧,需要的朋友可以参考下
    2018-01-01
  • APP中javascript+css3实现下拉刷新效果

    APP中javascript+css3实现下拉刷新效果

    本文给大家分享的是如何在APP中使用javascript结合CSS3实现下拉刷新特效的代码,非常的简单实用,有需要的小伙伴可以参考下。
    2016-01-01
  • 在 JavaScript 中保留小数点后两位的方法

    在 JavaScript 中保留小数点后两位的方法

    在 JavaScript 中,有多种方法可以保留小数点后两位,本文给大家分享比较常用的方法,文末给大家介绍了实现数据格式化保留两位小数的多种方法,感兴趣的朋友一起看看吧
    2023-10-10
  • js实现简单图片拖拽效果

    js实现简单图片拖拽效果

    这篇文章主要为大家详细介绍了js实现简单图片拖拽效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-02-02
  • TypeScript中类的基础概念和使用详解

    TypeScript中类的基础概念和使用详解

    这篇文章主要为大家详细介绍了TypeScript 中类的概念和用法,文中的示例代码讲解详细,希望能帮助大家更好地理解和运用 TypeScript 中的类
    2023-06-06
  • Javascript中String的常用方法实例分析

    Javascript中String的常用方法实例分析

    这篇文章主要介绍了Javascript中String的常用方法,实例分析了String常用的字符转换、截取、分割等技巧,需要的朋友可以参考下
    2015-06-06
  • 3分钟掌握常用的JS操作JSON方法总结

    3分钟掌握常用的JS操作JSON方法总结

    工作中做了几款自动化测试工具都是跟日志读取相关,日志格式又大多数都是JSON,所以这里把常用的JS操作JSON的方法做了总结
    2017-04-04
  • javascript实现yield的方法

    javascript实现yield的方法

    这篇文章介绍了javascript实现yield的方法,有需要的朋友可以参考一下
    2013-11-11

最新评论