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 中的各种边界情况。

相关推荐
成都渲染101云渲染66661 小时前
如何在3ds Max中实现更快、更高质量的渲染
前端·javascript·人工智能
闲猫3 小时前
Spring AI 对接Deepseek ChatModel 聊天对话
java·前端·spring
自信的未来5 小时前
JSON 工具|Web Worker 工程化打包 + 语法自动修复 + 多语言代码生成实战
java·前端·json
SRET5 小时前
Mineradio 沙盒隔离安装完全指南 | 一键安全运行 Electron 应用!!!
javascript·经验分享·安全·electron·aigc·音视频·ai编程
全栈前端老曹5 小时前
【MongoDB】安全与权限管理 —— 用户认证、角色权限、SSL 加密
前端·javascript·数据库·安全·mongodb·nosql·ssl
zhangxingchao6 小时前
AI大模型核心八:从 Agent Skill、长文档 RAG 到知识库更新与训练策略
前端·人工智能·后端
zhangxingchao6 小时前
AI大模型核心七:从 Workflow、RAG、记忆治理到幂等性
前端·人工智能·后端
gyx_这个杀手不太冷静7 小时前
Agent开发进阶指南(第 1 章):AI Agent 到底是什么?
前端·agent·ai编程
小小小小宇8 小时前
模型相关知识
前端
小小小小宇8 小时前
模型相关知识二
前端