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

相关推荐
昔冰_G6 分钟前
解锁webpack:对html、css、js及图片资源的抽离打包处理
前端·javascript·css·webpack·npm·html·打包
萌萌哒草头将军13 分钟前
🚀 REST API 还是 ✈️ GraphQL ❓
前端·vue.js·react.js
just小千26 分钟前
重学React(一):描述UI
前端·react.js·ui
fakaifa44 分钟前
【最新版】沃德代驾源码全开源+前端uniapp
前端·小程序·uni-app·开源·php·沃德代驾·代驾小程序
泯泷1 小时前
【SHA-2系列】SHA256 前端安全算法 技术实践
javascript·安全·node.js
清羽_ls1 小时前
leetcode-位运算
前端·算法·leetcode·位运算
李菠菜1 小时前
利用Nginx实现高性能的前端打点采集服务(支持GET和POST)
linux·前端·nginx
lilye661 小时前
精益数据分析(6/126):深入理解精益分析的核心要点
前端·人工智能·数据分析
Apifox1 小时前
Apifox 4月更新|Apifox在线文档支持LLMs.txt、评论支持使用@提及成员、支持为团队配置「IP 允许访问名单」
前端·后端·ai编程