用 == 被大佬喷了,我到底错哪了?

用 == 被大佬喷了,我到底错哪了?

在 JavaScript 中,===== 都是用来比较两个值是否相等的运算符,但它们的行为却有显著的区别。理解它们的差异对于写出健壮、可维护的代码至关重要。

一、==:相等运算符(宽松相等)

==相等运算符 ,也被称为宽松相等 (loose equality)。它在比较两个值时会自动进行类型转换(type coercion),然后再比较它们的值。

js 复制代码
console.log(1 == '1');     // true
console.log(0 == false);   // true
console.log(null == undefined); // true
console.log('' == 0);      // true

在这些例子中,尽管操作数的类型不同,但 == 会尝试将它们转换为相同的类型再进行比较。例如,字符串 '1' 会被转换为数字 1,布尔值 false 会被转换为数字 0

==类型转换规则

以下是 A == B 在比较不同类型时的一些常见转换规则:

类型 A 类型 B 转换方式
Number String 将 String 转换为 Number
Boolean 任何类型 将 Boolean 转换为 Number(0 或 1)
Object Primitive 调用 Object 的 valueOf()toString()
null undefined 相等
NaN 任何值 始终返回 false

对象与原始类型比较时的转换逻辑(Object == Primitive)

当对象与原始数据类型使用 == 进行比较时,js内部会调用对象中的**Symbol.toPrimitive(hint)、valueof()、toString()等方法**(若存在)将对象转换为原始值。

Symbol.toPrimitive(hint)中,hint是要转换成的类型,当比较一个对象(obj)和一个原始值(如 1'hello')时,JavaScript 会根据目标类型 调用 Symbol.toPrimitive 并传入相应的 hint(提示类型)。

具体转换的执行流程如下

  1. 传入提示类型,调用Symbol.toPrimitive方法(方法需要存在)
  2. 如果没有 Symbol.toPrimitive,则会尝试调用 valueOf() 方法
  3. 如果valueOf() 方法返回的是原始值,则使用这个值。如果返回的仍是对象,则调用 toString()
  4. 调用toString()方法若返回的是原始值(通常是字符串),则使用这个值。否则,抛出 TypeError

示例:

Symbol.toPrimitive 优先

js 复制代码
let obj = {
  [Symbol.toPrimitive](hint) {
    if (hint === 'number') return 1;
    if (hint === 'string') return 'hello';
    return 'default';
  }
};

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

对象里面没有Symbol.toPrimitive ,调用valueOf

js 复制代码
let obj = {
  valueOf() {
    return 1;
  }
};

console.log(obj == 1); // true

没有 Symbol.toPrimitivevalueOf(),调用 toString()

js 复制代码
let obj = {
  toString() {
    return 'object';
  }
};

console.log(obj == 'object'); // true

[] == ![] 的结果是什么?为什么?

在 JavaScript 中,表达式:

js 复制代码
console.log([] == ![]) // true

虽然看起来非常反直觉,但我们可以一步一步地拆解这个过程,理解 JavaScript 的类型转换机制。

  1. ![] 是什么?

    逻辑非运算符 ! 会将操作数转换为布尔值,再取反。

    • 空数组 [] 是一个对象,在布尔上下文中被视为 true 。所以 ![] 的结果是:
    js 复制代码
    !Boolean([]) → false

    因此,表达式可以等价为:

    js 复制代码
    [] == false
  2. [] == false 的比较过程

    现在我们来分析这个比较:[] == false

    根据 == 的类型转换规则:布尔值转为数字false 转换为数字 0。对象转为原始值,而(空数组)是一个对象,它会被尝试转换为字符串,再转换为数字。

    • []""(空字符串)调用的是toString()

    • ""0(通过 Number("") 转换)

      转换过程

    js 复制代码
    [] == false
    → [] == 0
    → "" == 0
    → 0 == 0
    → true

虽然 [] == ![] 看起来像是"自己不等于自己",但实际上是因为 JavaScript 在 == 比较中进行了多次类型转换。这体现了 ==类型转换机制虽然强大,但也可能导致难以理解的行为。

为了避免这种令人困惑的结果,推荐使用 === 来进行比较,以避免类型转换带来的副作用。

二、===:全等运算符(严格相等)

===全等运算符 ,也被称为严格相等 (strict equality)。它不会进行类型转换,只有当两个值的类型和值都相等 时,才会返回 true

js 复制代码
console.log(1 === '1');     // false
console.log(0 === false);   // false
console.log(null === undefined); // false
console.log('' === 0);      // false
console.log(5 === 5);       // true
console.log(NaN === NaN);   // false

在这些例子中,即使值在类型转换后是相等的,=== 也会因为类型不同而返回 false

特殊情况:

  • NaN === NaN 返回 false,这是 JavaScript 中的一个特殊规则。
  • +0 === -0 返回 true,尽管它们在底层表示不同。

在大多数现代 JavaScript 开发中,推荐使用 ===,因为它不会进行类型转换,避免了因类型转换带来的意外行为 。使用 === 可以提高代码的可读性和可维护性。

例外情况:

  • 在某些特定的场景下使用 ==可能更方便,例如:

    js 复制代码
    if (value == null) {
      // 处理 null 或 undefined 的情况
    }

    这里的当value 会会同时匹配,这在某些逻辑判断中非常有用。

Object.is() 的使用

如果你需要一种更严格、更准确 的比较方式,JavaScript 提供了 Object.is() 方法,它可以处理一些特殊值的比较,Object.is() 不会进行类型转换,也不会像 == 一样做隐式转换。

js 复制代码
Object.is(NaN, NaN);     // true
Object.is(+0, -0);       // false
Object.is([], []);       // false(引用不同)

Object.is() 底层代码逻辑

js 复制代码
function is(a,b){
    if(a === b){ // 若 a = -0 ,b = +0  那么 a/0 为-Infinity  b/0 为+Infinity  ,返回false
        return a !== 0 || b !== 0 || a/0 === b/0
    }else {
        return a !== a && b !== b  // 当 a与b都是 NaN时 返回true
    }
}

总结

比较方式 名称 是否类型转换 推荐使用场景
== 相等运算符 特定逻辑判断(如 null/undefined)
=== 全等运算符 推荐在大多数情况下使用
相关推荐
小屁孩大帅-杨一凡1 分钟前
如何使用markdownify库将HTML转换为Markdown?
开发语言·前端·python·html
啃火龙果的兔子2 小时前
修改 Lucide-React 图标样式的方法
前端·react.js·前端框架
前端 贾公子3 小时前
为何在 Vue 的 v-model 指令中不能使用可选链(Optional Chaining)?
前端·javascript·vue.js
潘多拉的面3 小时前
Vue的ubus emit/on使用
前端·javascript·vue.js
遗憾随她而去.3 小时前
js面试题 高频(1-11题)
开发语言·前端·javascript
hqxstudying5 小时前
J2EE模式---前端控制器模式
java·前端·设计模式·java-ee·状态模式·代码规范·前端控制器模式
开开心心就好7 小时前
Excel数据合并工具:零门槛快速整理
运维·服务器·前端·智能手机·pdf·bash·excel
im_AMBER7 小时前
Web开发 05
前端·javascript·react.js
Au_ust7 小时前
HTML整理
前端·javascript·html
安心不心安7 小时前
npm全局安装后,依然不是内部或外部命令,也不是可运行的程序或批处理文件
前端·npm·node.js