(a==1 && a==2 && a==3) true? JavaScript 的魔法

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:引爆 Stack Overflow 的问题

\n\n

想象一下这个场景:你在面试,面试官带着狡黠的微笑问道:“表达式 a == 1 && a == 2 && a == 3 有没有可能返回 true?”。第一反应是——“当然不可能!一个变量不可能同时等于三个不同的数字。”

\n\n

然而,如果你对 JavaScript 有更深入的了解,你就会知道:这门语言中可能发生最意想不到的事情。这个问题在 Stack Overflow 上获得了数千次浏览,并成为检验对 JS 中 类型转换、getter 和 对象包装器 理解的经典问题。

\n\n

在本文中,我们不仅要证明这是可能的,还将分析 5 种不同的方法,让 (a==1 && a==2 && a==3) 返回 true。你将了解 非严格比较 (==) 的工作原理,它与严格比较 (===) 的区别,以及如何控制它。

\n\n

本文既适合希望更深入理解 JavaScript 的初学者,也适合准备面试的经验丰富的开发者。开始吧!

\n\n

方法 #1:valueOf 的魔法(最流行)

\n\n

最优雅且最常被引用的方法是使用一个 自定义对象,并重写其 valueOf 方法。在 JavaScript 中,当你通过 == 将对象与原始值进行比较时,引擎会自动调用 valueOf()(或 toString())将对象转换为原始值。

\n\n

我们将创建一个计数器对象,每次调用 valueOf 时,其值都会增加 1。

\n\n
const a = {\n  value: 1,\n  valueOf() {\n    return this.value++;\n  }\n};\n\nconsole.log(a == 1 && a == 2 && a == 3); // true
\n\n

工作原理:

\n
    \n
  • 在第一次比较 a == 1 时,调用 valueOf(),返回 1,并且 value 增加到 2。
  • \n
  • 在第二次比较 a == 2 时,再次调用 valueOf(),返回 2,value 变为 3。
  • \n
  • 第三次比较 a == 3 成功完成链条。
  • \n
\n\n

这是 类型强制转换 在行动中的完美示例。正是这种机制使 JavaScript 同时成为一种灵活且危险的语言。

\n\n

方法 #2:toString——valueOf 的孪生兄弟

\n\n

如果你重写 toString 而不是 valueOf,结果是一样的。在非严格比较中,JavaScript 可以使用这两种方法中的任何一种来转换对象。

\n\n
const a = {\n  value: 1,\n  toString() {\n    return this.value++;\n  }\n};\n\nconsole.log(a == 1 && a == 2 && a == 3); // true
\n\n

细微的区别在于:对于数值比较,valueOf 具有优先级;而对于字符串比较,toString 具有优先级。但在我们的例子中,两者都非常适用。

\n\n

方法 #3:全局作用域中的 Getter

\n\n

许多人甚至没有意识到,JavaScript 中的 全局变量 实际上是全局对象(浏览器中的 window,Node.js 中的 global)的属性。而属性可以通过 getter 来定义。

\n\n

我们可以直接在全局对象上为变量 a 定义一个 getter:

\n\n
let value = 1;\nObject.defineProperty(window, 'a', {\n  get() {\n    return value++;\n  }\n});\n\nconsole.log(a == 1 && a == 2 && a == 3); // true
\n\n

这个方法特别有趣,因为 a 表现得像一个普通变量,尽管它实际上是一个带有 getter 的属性。每次你访问 a 时,getter 都会返回一个新值。

博客

书籍推荐