JavaScript

在 JavaScript 里,如何比较两个变量是否相等?我们通常会使用 == 运算符。

a == b

那么,下面的代码会输出什么呢?

1
2
3
apple = [1, 2];
banana = [1, 2];
console.log(apple == banana)

竟然是 false!你说,这怎么可能?它们俩明明是一样的呀。

实际上,JavaScript 的文档详细介绍了 == 的用途。在进行抽象相等比较(Abstract Equality Comparison)时,如果两个变量的类型都是 Object(对象,包括数组),JavaScript 会检查这两个变量是否指向同一个对象(引用)。只有当它们引用内存中的同一个地址时,比较结果才为 true。

我们回头看 apple = [1, 2],正如它所表达出来的一样:“定义一个数组,它的名字叫 apple”。这个数组会被系统分配到内存的某个位置。apple 就像是一张写着数组所在内存位置的名片(或者说指针),它仅仅是这个内存地址的引用。所以,当我们定义 apple = [1, 2]; banana = [1, 2]; 的时候,一个数组会被储存到内存某个位置,可以通过 apple 找到它;另一个数组也会被存到内存的另一个不同位置,可以通过 banana 来找到它。这两个数组虽然元素和内容都完全一样,但却是储存在内存不同位置的两个独立的数组对象。在进行比较的时候,它们俩不是同一个对象,所以结果是 false。

我们再来看看另一个比较:

1
2
3
4
apple = {"name": "Alice"};
banana = apple;
apple = {"name": "Alice"};
console.log(apple == banana); // false

猜一下发生了什么,为什么是 false?

  1. 我们定义了一个哈希(对象),变量 apple 指向了它。
  2. 我们又定义了一个变量 banana,它指向了 apple 所指向的同一个哈希对象(引用复制)。
  3. 现在,我们定义一个新的、和之前一模一样的哈希对象,并且让 apple 指向这个新的对象。
    如此,applebanana 指向的数据便是不同内存位置的两个相同内容的哈希。它们虽然内容一样,但不是同一个对象,因此 apple == banana 返回 false。

结论:对于严格(===)或抽象(==)比较,两个独立的(distinct)对象永远不会相等。

我们看看 Python 和 Ruby 语言有没有这个问题。

Ruby

Ruby 里面有几种比较方法。一种是 equal?() 方法。这个方法是由 BasicObject(所有其它类的父类)所定义的,用于比较两个对象是否是同一个实例(即引用相等)。

1
2
3
4
5
6
7
a = [1, 2, 3]
b = [1, 2, 3]
c = a
p [1, 2, 3] == [1, 2, 3] # true
p [1, 2, 3].equal?([1, 2, 3]) # false
p a.equal?(b) # false
p a.equal?(c) # true

当我们用 == 比较两个数组时,Ruby 会逐个比较数组中的元素(即值相等)。只有当每个元素都相等,并且在数组中的位置也一样时,两个数组才相等。但当我们用 equal? 函数比较两个数组,除非它们所指的、是同一个内存中的对象,否则结果就是 false。

此外,Ruby 还提供了 eql? 方法来进行更为严格的比较。它会将两个对象的哈希值(hash value)进行比较,通常在判断集合(Set)中的唯一性或哈希(Hash)的键(Key)时使用。大多数 Ruby 的可比较对象都有一个 .hash 方法,该方法返回的哈希值被 eql? 用于比较两个对象。

Python

Python 里面用 is 运算符来确定两个变量是否指向同一个对象(引用相等),而使用 == 运算符来比较对象的内容是否相等(值相等)。例如:

1
2
3
4
5
6
a = [1, 2, 3]
b = [1, 2, 3]
c = a
print(a == b) # True
print(a is b) # False
print(a is c) # True

Python 的 is 运算符在行为上类似于 JavaScript 的全等 === 运算符用于比较 Object 类型的情形,或者 Ruby 的 equal? 方法。如果比较的两个变量所指的不是同一个对象,is 的返回值就是 false,即使它们的内容完全一致。

总结

  1. JavaScript 的对象比较:在 JavaScript 中,无论是使用 == 还是 === 比较两个 Object 类型(如 Array、Object)时,比较的都是引用(Reference)。如果它们不是同一个对象实例,即使内容完全相同,比较结果也是 false。JavaScript 原生并没有直接进行深度值比较(Deep Value Comparison)的运算符。
  2. 术语区别
    • 值相等(Value Equality / Structural Equality):比较对象的内容(如 Python 的 ==,Ruby 的 ==)。
    • 引用相等(Reference Equality / Identity):比较两个变量是否指向内存中的同一个对象(如 JavaScript 的 ==/=== 用于对象,Python 的 is,Ruby 的 equal?)。