Can (a== 1 && a ==2 && a==3) ever evaluate to true?

Can (a== 1 && a ==2 && a==3) ever evaluate to true?

技术背景

在常规编程思维中,一个变量 a 不能同时等于 1、2 和 3。但在 JavaScript 及其他一些编程语言中,利用语言的特性,如类型转换、特殊字符、代理等,是可以让 (a== 1 && a ==2 && a==3) 表达式评估为 true 的。这不仅是一个有趣的编程挑战,也能帮助开发者更深入地理解语言的底层机制。

实现步骤

JavaScript 实现方式

1. 自定义 toStringvalueOf 方法

1
2
3
4
5
6
7
8
9
10
const a = {
i: 1,
toString: function () {
return a.i++;
}
}

if (a == 1 && a == 2 && a == 3) {
console.log('Hello World!');
}

当使用松散相等运算符 == 时,如果一边是对象,另一边是数字,引擎会尝试将对象转换为数字,先调用 valueOf,若不可调用则调用 toString

2. 利用 Unicode 特殊字符创建不同变量

1
2
3
4
5
6
var a = 1;
var a = 2;
var а = 3;
if (a == 1 && a == 2 && а == 3) {
console.log("Why hello there!");
}

这里的 aа 是不同的 Unicode 字符,虽然看起来相似,但在代码中是不同的变量。

3. 使用 with 语句和 getter

1
2
3
4
5
6
7
8
9
var i = 0;
with ({
get a() {
return ++i;
}
}) {
if (a == 1 && a == 2 && a == 3)
console.log("wohoo");
}

with 语句中的 getter 会让 a 每次返回不同的值。

4. 利用 Symbol.toPrimitive

1
2
3
let i = 0;
let a = { [Symbol.toPrimitive]: () => ++i };
console.log(a == 1 && a == 2 && a == 3);

Symbol.toPrimitive 是 ES6 中用于对象转换为原始值的方法。

5. 使用正则表达式

1
2
3
4
5
6
7
8
9
10
var a = {
r: /\d/g,
valueOf: function () {
return this.r.exec(123)[0]
}
}

if (a == 1 && a == 2 && a == 3) {
console.log("!");
}

自定义 valueOf 方法,利用正则表达式的 g 标志,每次调用 exec 会返回不同的值。

6. 使用 Proxy

1
2
3
4
var a = new Proxy({ i: 0 }, {
get: (target, name) => name === Symbol.toPrimitive ? () => ++target.i : target[name],
});
console.log(a == 1 && a == 2 && a == 3);

Proxy 可以拦截对象的操作,这里拦截了类型转换时的操作。

其他语言实现方式

Ruby

1
2
3
4
5
6
7
8
9
10
class A
def ==(o)
true
end
end

a = A.new
if a == 1 && a == 2 && a == 3
puts "Don't do this!"
end

自定义 == 方法,让对象与任何值比较都返回 true

Python

1
2
3
4
5
6
7
8
class A:
def __eq__(self, who_cares):
return True


a = A()
if a == 1 and a == 2 and a == 3:
print("Don't do that!")

自定义类的 __eq__ 方法,实现与任何值比较都为 True

Java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.lang.reflect.Field;

public class IntegerMess {
public static void main(String[] args) throws Exception {
Field valueField = Integer.class.getDeclaredField("value");
valueField.setAccessible(true);
valueField.setInt(1, valueField.getInt(42));
valueField.setInt(2, valueField.getInt(42));
valueField.setInt(3, valueField.getInt(42));
valueField.setAccessible(false);

Integer a = 42;
if (a.equals(1) && a.equals(2) && a.equals(3)) {
System.out.println("Bad idea.");
}
}
}

通过反射修改 Integer 类的缓存值,实现条件判断为 true

核心代码

以下是几种常见的 JavaScript 实现的核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 自定义 toString 方法
const a = {
i: 1,
toString: function () {
return a.i++;
}
}

// 使用 with 语句和 getter
var i = 0;
with ({
get a() {
return ++i;
}
}) {
if (a == 1 && a == 2 && a == 3)
console.log("wohoo");
}

// 利用 Symbol.toPrimitive
let i = 0;
let a = { [Symbol.toPrimitive]: () => ++i };
console.log(a == 1 && a == 2 && a == 3);

最佳实践

虽然这些技巧可以让 (a== 1 && a ==2 && a==3) 评估为 true,但在实际开发中应避免使用,因为它们会使代码难以理解和维护。在面试中遇到这类问题,应展示对语言特性的理解,并强调在实际项目中会使用清晰、可维护的代码。

常见问题

为什么这些方法在严格相等 === 时不适用?

严格相等 === 不会进行类型转换,而上述很多方法依赖于松散相等 == 的类型转换机制,所以在严格相等时无法达到预期效果。

这些技巧会带来什么问题?

这些技巧会使代码的可读性和可维护性变差,增加调试难度,容易引入难以发现的错误。在团队协作开发中,可能会给其他开发者带来困扰。


Can (a== 1 && a ==2 && a==3) ever evaluate to true?
https://119291.xyz/posts/2025-05-13.can-a-equal-1-and-a-equal-2-and-a-equal-3-evaluate-to-true/
作者
ww
发布于
2025年5月13日
许可协议