Lodash源码阅读-compareAscending

Lodash 源码阅读-compareAscending

概述

compareAscending 是 Lodash 内部的一个比较函数,用于实现升序排序。它能处理 JavaScript 中各种特殊值的比较(如 nullundefinedSymbol 等),确保排序结果符合预期。

前置学习

  • 依赖函数:

    • isSymbol: 用于判断一个值是否为 Symbol 类型
  • 技术知识:

    • JavaScript 中的值比较
    • JavaScript 中的特殊值(undefined、null、NaN、Symbol)
    • 反射性质(reflexivity):即值是否等于自身,NaN 是唯一不等于自身的值

源码实现

js 复制代码
function compareAscending(value, other) {
  if (value !== other) {
    var valIsDefined = value !== undefined,
      valIsNull = value === null,
      valIsReflexive = value === value,
      valIsSymbol = isSymbol(value);

    var othIsDefined = other !== undefined,
      othIsNull = other === null,
      othIsReflexive = other === other,
      othIsSymbol = isSymbol(other);

    if (
      (!othIsNull && !othIsSymbol && !valIsSymbol && value > other) ||
      (valIsSymbol &&
        othIsDefined &&
        othIsReflexive &&
        !othIsNull &&
        !othIsSymbol) ||
      (valIsNull && othIsDefined && othIsReflexive) ||
      (!valIsDefined && othIsReflexive) ||
      !valIsReflexive
    ) {
      return 1;
    }
    if (
      (!valIsNull && !valIsSymbol && !othIsSymbol && value < other) ||
      (othIsSymbol &&
        valIsDefined &&
        valIsReflexive &&
        !valIsNull &&
        !valIsSymbol) ||
      (othIsNull && valIsDefined && valIsReflexive) ||
      (!othIsDefined && valIsReflexive) ||
      !othIsReflexive
    ) {
      return -1;
    }
  }
  return 0;
}

实现思路

compareAscending 函数实现了一个稳定的比较逻辑,用于确定两个值的大小关系。它的核心思路是:

  1. 首先判断两个值是否相等,相等则直接返回 0
  2. 对两个值的特性进行分析(是否为 undefined、null、Symbol、NaN)
  3. 根据值的特性和大小关系,决定返回 1(第一个值更大)或 -1(第一个值更小)
  4. 考虑特殊情况的处理,确保排序稳定性

源码解析

函数一开始进行相等性检查:

js 复制代码
if (value !== other) {
  // 如果不相等,进入复杂比较逻辑
} else {
  return 0; // 如果完全相等,直接返回0表示相等
}

接下来定义了 8 个变量来标记两个比较值的特性:

js 复制代码
// 对第一个值的特性分析
var valIsDefined = value !== undefined, // 值不是undefined
  valIsNull = value === null, // 值是null
  valIsReflexive = value === value, // 值等于自身(排除NaN)
  valIsSymbol = isSymbol(value); // 值是Symbol类型

// 对第二个值做同样的特性分析
var othIsDefined = other !== undefined,
  othIsNull = other === null,
  othIsReflexive = other === other,
  othIsSymbol = isSymbol(other);

这里特别注意valIsReflexiveothIsReflexive的作用是检测 NaN,因为在 JavaScript 中只有 NaN 不等于自身:NaN === NaN返回false

然后是两个复杂的条件判断,第一个判断何时返回 1(表示 value 大于 other):

js 复制代码
if (
  // 条件1: 普通值比较,value确实大于other
  (!othIsNull && !othIsSymbol && !valIsSymbol && value > other) ||
  // 条件2: value是Symbol,而other是普通值(非null/Symbol/undefined/NaN)
  (valIsSymbol &&
    othIsDefined &&
    othIsReflexive &&
    !othIsNull &&
    !othIsSymbol) ||
  // 条件3: value是null,other是除undefined和NaN外的值
  (valIsNull && othIsDefined && othIsReflexive) ||
  // 条件4: value是undefined,other不是NaN
  (!valIsDefined && othIsReflexive) ||
  // 条件5: value是NaN
  !valIsReflexive
) {
  return 1;
}

第二个判断何时返回-1(表示 value 小于 other):

js 复制代码
if (
  // 条件1: 普通值比较,value确实小于other
  (!valIsNull && !valIsSymbol && !othIsSymbol && value < other) ||
  // 条件2: other是Symbol,而value是普通值
  (othIsSymbol &&
    valIsDefined &&
    valIsReflexive &&
    !valIsNull &&
    !valIsSymbol) ||
  // 条件3: other是null,value是除undefined和NaN外的值
  (othIsNull && valIsDefined && valIsReflexive) ||
  // 条件4: other是undefined,value不是NaN
  (!othIsDefined && valIsReflexive) ||
  // 条件5: other是NaN
  !othIsReflexive
) {
  return -1;
}

让我们详细拆解这些条件:

返回 1 的条件 1!othIsNull && !othIsSymbol && !valIsSymbol && value > other

  • 确保 other 不是 null
  • 确保 other 不是 Symbol
  • 确保 value 不是 Symbol
  • 然后用普通的大于号比较 value 和 other

例如:compareAscending(5, 3) 满足这个条件,返回 1

返回 1 的条件 2valIsSymbol && othIsDefined && othIsReflexive && !othIsNull && !othIsSymbol

  • value 是 Symbol
  • other 不是 undefined
  • other 不是 NaN
  • other 不是 null
  • other 不是 Symbol

例如:compareAscending(Symbol(), 42) 满足这个条件,返回 1(表示 Symbol > 普通值)

返回 1 的条件 3valIsNull && othIsDefined && othIsReflexive

  • value 是 null
  • other 不是 undefined
  • other 不是 NaN

例如:compareAscending(null, 42) 满足这个条件,返回 1(表示 null > 普通值)

返回 1 的条件 4!valIsDefined && othIsReflexive

  • value 是 undefined
  • other 不是 NaN

例如:compareAscending(undefined, null) 满足这个条件,返回 1(表示 undefined > null)

返回 1 的条件 5!valIsReflexive

  • value 是 NaN

例如:compareAscending(NaN, undefined) 满足这个条件,返回 1(表示 NaN > undefined)

对于返回-1 的条件,逻辑是类似的,只是互换了 value 和 other 的角色。

总结这个函数实现的排序规则:

javascript 复制代码
NaN > undefined > null > Symbol > 普通值

普通值之间按照 JavaScript 内置的比较规则排序。这种排序策略确保了稳定性,并处理了 JavaScript 中所有可能的值类型。

总结

compareAscending 函数巧妙地解决了 JavaScript 中各种特殊值的比较问题,遵循了以下排序规则:

  • 普通值(如数字、字符串)按自然顺序排序
  • 特殊值按 NaN > undefined > null > Symbol > 普通值 的顺序排序

这个函数充分体现了 Lodash 处理边界情况的严谨性,在实际开发中,我们也应该注意处理各种特殊值的比较逻辑,以确保程序的健壮性。通过学习这个函数,我们可以了解如何设计一个完善的比较函数,以及如何考虑 JavaScript 中的各种边界情况。

相关推荐
帅帅哥的兜兜5 分钟前
next.js实现项目搭建
前端·react.js·next.js
筱歌儿10 分钟前
css 左右布局
前端·css
GISer_Jing35 分钟前
编译原理AST&以Babel为例进行解读、Webpack中自定义loader与plugin
前端·webpack·node.js
GISer_Jing36 分钟前
Webpack中Compiler详解以及自定义loader和plugin详解
前端·webpack·node.js
浩~~1 小时前
CSS常用选择器
前端·css
于慨1 小时前
uniapp+vite+cli模板引入tailwindcss
前端·uni-app
yunvwugua__1 小时前
Python训练营打卡 Day26
前端·javascript·python
满怀10151 小时前
【Django全栈开发实战】从零构建企业级Web应用
前端·python·django·orm·web开发·前后端分离
Darling02zjh2 小时前
GUI图形化演示
前端
Channing Lewis2 小时前
如何判断一个网站后端是用什么语言写的
前端·数据库·python