JavaScript
在 JavaScript 里,如何比较两个变量是否相等?我们通常会使用 == 运算符。
a == b |
那么,下面的代码会输出什么呢?
1 | apple = [1, 2]; |
竟然是 false!你说,这怎么可能?它们俩明明是一样的呀。
实际上,JavaScript 的文档详细介绍了 == 的用途。在进行抽象相等比较(Abstract Equality Comparison)时,如果两个变量的类型都是 Object(对象,包括数组),JavaScript 会检查这两个变量是否指向同一个对象(引用)。只有当它们引用内存中的同一个地址时,比较结果才为 true。
我们回头看 apple = [1, 2],正如它所表达出来的一样:“定义一个数组,它的名字叫 apple”。这个数组会被系统分配到内存的某个位置。apple 就像是一张写着数组所在内存位置的名片(或者说指针),它仅仅是这个内存地址的引用。所以,当我们定义 apple = [1, 2]; banana = [1, 2]; 的时候,一个数组会被储存到内存某个位置,可以通过 apple 找到它;另一个数组也会被存到内存的另一个不同位置,可以通过 banana 来找到它。这两个数组虽然元素和内容都完全一样,但却是储存在内存不同位置的两个独立的数组对象。在进行比较的时候,它们俩不是同一个对象,所以结果是 false。
我们再来看看另一个比较:
1 | apple = {"name": "Alice"}; |
猜一下发生了什么,为什么是 false?
- 我们定义了一个哈希(对象),变量
apple指向了它。 - 我们又定义了一个变量
banana,它指向了apple所指向的同一个哈希对象(引用复制)。 - 现在,我们定义一个新的、和之前一模一样的哈希对象,并且让
apple指向这个新的对象。
如此,apple和banana指向的数据便是不同内存位置的两个相同内容的哈希。它们虽然内容一样,但不是同一个对象,因此apple == banana返回 false。
结论:对于严格(
===)或抽象(==)比较,两个独立的(distinct)对象永远不会相等。
我们看看 Python 和 Ruby 语言有没有这个问题。
Ruby
Ruby 里面有几种比较方法。一种是 equal?() 方法。这个方法是由 BasicObject(所有其它类的父类)所定义的,用于比较两个对象是否是同一个实例(即引用相等)。
1 | a = [1, 2, 3] |
当我们用 == 比较两个数组时,Ruby 会逐个比较数组中的元素(即值相等)。只有当每个元素都相等,并且在数组中的位置也一样时,两个数组才相等。但当我们用 equal? 函数比较两个数组,除非它们所指的、是同一个内存中的对象,否则结果就是 false。
此外,Ruby 还提供了 eql? 方法来进行更为严格的比较。它会将两个对象的哈希值(hash value)进行比较,通常在判断集合(Set)中的唯一性或哈希(Hash)的键(Key)时使用。大多数 Ruby 的可比较对象都有一个 .hash 方法,该方法返回的哈希值被 eql? 用于比较两个对象。
Python
Python 里面用 is 运算符来确定两个变量是否指向同一个对象(引用相等),而使用 == 运算符来比较对象的内容是否相等(值相等)。例如:
1 | a = [1, 2, 3] |
Python 的 is 运算符在行为上类似于 JavaScript 的全等 === 运算符用于比较 Object 类型的情形,或者 Ruby 的 equal? 方法。如果比较的两个变量所指的不是同一个对象,is 的返回值就是 false,即使它们的内容完全一致。
总结
- JavaScript 的对象比较:在 JavaScript 中,无论是使用
==还是===比较两个 Object 类型(如 Array、Object)时,比较的都是引用(Reference)。如果它们不是同一个对象实例,即使内容完全相同,比较结果也是 false。JavaScript 原生并没有直接进行深度值比较(Deep Value Comparison)的运算符。 - 术语区别:
- 值相等(Value Equality / Structural Equality):比较对象的内容(如 Python 的
==,Ruby 的==)。 - 引用相等(Reference Equality / Identity):比较两个变量是否指向内存中的同一个对象(如 JavaScript 的
==/===用于对象,Python 的is,Ruby 的equal?)。
- 值相等(Value Equality / Structural Equality):比较对象的内容(如 Python 的