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

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

在 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)
=== 全等运算符 推荐在大多数情况下使用
相关推荐
去伪存真6 分钟前
大模型的参数量为什么那么大?
前端·人工智能
IT_陈寒11 分钟前
Java空指针这次真把我坑惨了
前端·人工智能·后端
高级程序源1 小时前
django大学生创新创业项目管理系统94923-计算机课程设计、毕业设计
javascript·vue.js·spring boot·后端·python·django·课程设计
八荒启·交互动画1 小时前
# Web特效020—让 Web 特效真正动起来:时间循环应该怎么接
前端·webgl·网页特效·八荒启-交互动画·八荒启
动恰客流统计2 小时前
线下零售数字化浪潮下,客流统计的3个核心发展趋势
大数据·前端·人工智能
lizz22762 小时前
Babel插件手册
javascript
用户938515635072 小时前
从小米前端面试题,彻底搞懂闭包、作用域链与 useState 惰性初始化
前端·面试
粥里有勺糖3 小时前
视野修炼第133期 | Native 回春了?
前端·github·agent
aichitang20243 小时前
前端小skill
前端·人工智能·算法·ai·前端框架
前端·柱子4 小时前
q-floodfill白边锯齿?一招搞定抗锯齿边缘问题
前端·html·canva可画