功能概述
eq 函数是 Lodash 中的一个基础工具函数,主要用于比较两个值是否相等。它实现了 ECMAScript 规范中的 SameValueZero
比较算法,不仅能处理普通的相等性比较,还能正确处理 JavaScript 中的特殊情况,如 NaN
与自身的比较。这个函数虽然看起来简单,但它解决了 JavaScript 中 ===
操作符无法正确处理 NaN
相等性的问题,是 Lodash 中许多其他比较函数的基础。
源码实现
js
function eq(value, other) {
return value === other || (value !== value && other !== other);
}
实现原理解析
原理概述
eq 函数的实现原理非常巧妙:它首先使用严格相等操作符 ===
比较两个值,如果相等则直接返回 true
;如果不相等,则检查两个值是否都是 NaN
(通过 value !== value
这一特性判断),如果都是 NaN
则也返回 true
。这种实现方式完美地符合了 ECMAScript 的 SameValueZero
算法规范,既保持了 ===
的高性能,又解决了它无法正确处理 NaN
的问题。
整个过程可以概括为:
- 使用严格相等操作符
===
比较两个值 - 如果相等,返回
true
- 如果不相等,检查两个值是否都是
NaN
- 如果都是
NaN
,返回true
;否则返回false
代码解析
js
return value === other || (value !== value && other !== other);
这一行代码看似简单,但包含了两个关键部分:
1. 严格相等比较:value === other
首先使用 JavaScript 的严格相等操作符 ===
比较两个值。严格相等操作符具有以下特性:
- 不进行类型转换,类型不同的值总是不相等
- 对象(包括数组)只有在引用同一个对象时才相等
undefined
只与undefined
相等,null
只与null
相等+0
与-0
被视为相等NaN
与任何值(包括自身)都不相等
示例:
js
// 基本类型比较
eq(1, 1); // true,数字相等
eq("a", "a"); // true,字符串相等
eq(true, true); // true,布尔值相等
// 不同类型比较
eq(1, "1"); // false,类型不同
eq(0, false); // false,类型不同
// 对象比较
var obj = { a: 1 };
eq(obj, obj); // true,同一引用
eq(obj, { a: 1 }); // false,不同引用
// 特殊值比较
eq(null, null); // true
eq(undefined, undefined); // true
eq(+0, -0); // true,在 === 中 +0 和 -0 相等
2. NaN 处理:(value !== value && other !== other)
这部分代码利用了 JavaScript 中 NaN
的一个独特特性:NaN
是唯一一个自身不等于自身的值。也就是说,NaN !== NaN
总是返回 true
。
因此,表达式 value !== value
只有在 value
是 NaN
时才为 true
。同样,other !== other
只有在 other
是 NaN
时才为 true
。
整个表达式 (value !== value && other !== other)
只有在两个值都是 NaN
时才返回 true
。
示例:
js
// NaN 比较
eq(NaN, NaN); // true,两个值都是 NaN
eq(NaN, 1); // false,只有一个值是 NaN
eq(1, NaN); // false,只有一个值是 NaN
// 验证 NaN 的特性
NaN === NaN; // false,严格相等操作符无法正确处理 NaN
NaN !== NaN; // true,NaN 不等于自身
使用示例
js
// 基本类型比较
_.eq(1, 1);
// => true
_.eq("a", "a");
// => true
_.eq(true, true);
// => true
// 不同类型比较
_.eq(1, "1");
// => false
_.eq(0, false);
// => false
// 对象比较
var object = { a: 1 };
var other = { a: 1 };
_.eq(object, object);
// => true
_.eq(object, other);
// => false
// 特殊值比较
_.eq(NaN, NaN);
// => true
_.eq(+0, -0);
// => true
_.eq(null, null);
// => true
_.eq(undefined, undefined);
// => true
// 包装对象与原始值比较
_.eq("a", Object("a"));
// => false
_.eq(1, Object(1));
// => false
与其他方法的比较
- eq vs === :与严格相等操作符相比,eq 能正确处理
NaN
的比较。
js
// 严格相等操作符无法正确处理 NaN
NaN === NaN; // false
// eq 函数能正确处理 NaN
_.eq(NaN, NaN); // true
- eq vs ==:与相等操作符相比,eq 不进行类型转换。
js
// 相等操作符会进行类型转换
1 == "1"; // true
0 == false; // true
// eq 不进行类型转换
_.eq(1, "1"); // false
_.eq(0, false); // false
- eq vs isEqual:eq 只进行浅比较,而 isEqual 进行深比较。
js
// eq 只进行浅比较
_.eq({ a: 1 }, { a: 1 }); // false,不同引用
// isEqual 进行深比较
_.isEqual({ a: 1 }, { a: 1 }); // true,内容相同
js
// Object.is 区分 +0 和 -0
Object.is(+0, -0); // false
// eq 不区分 +0 和 -0
_.eq(+0, -0); // true
注意事项
- 对象比较 :eq 只进行引用比较,不比较对象的内容。如果需要比较对象内容,应使用
_.isEqual
。
js
// eq 只比较引用
_.eq({ a: 1 }, { a: 1 }); // false,不同引用
// isEqual 比较内容
_.isEqual({ a: 1 }, { a: 1 }); // true,内容相同
- 原始值与包装对象:eq 区分原始值和其对应的包装对象。
js
// 原始字符串与字符串对象
_.eq("a", Object("a")); // false
// 原始数字与数字对象
_.eq(1, Object(1)); // false
- +0 与 -0:eq 将 +0 和 -0 视为相等,这在大多数情况下是符合预期的,但在某些特殊场景可能需要区分它们。
js
// eq 不区分 +0 和 -0
_.eq(+0, -0); // true
// 如果需要区分,可以使用 Object.is
Object.is(+0, -0); // false
总结
Lodash 的 eq 函数是一个简单但非常实用的工具,它通过巧妙的实现解决了 JavaScript 中 ===
操作符无法正确处理 NaN
相等性的问题。虽然只有一行代码,但它完美地实现了 ECMAScript 的 SameValueZero
算法,为 Lodash 中的其他比较函数提供了基础。