Lodash源码阅读-getMatchData

Lodash 源码阅读-getMatchData

概述

getMatchData 是 Lodash 内部的一个工具函数,简单来说就是把对象的键值对变成特殊格式的数组。它把普通的对象 {a: 1, b: "hello"} 转换成 [["a", 1, true], ["b", "hello", true]] 这样的结构,主要是为了让 Lodash 内部的对象匹配功能(比如 _.isMatch)能更高效地工作。

前置学习

依赖函数

  • keys:获取对象自身的可枚举属性名数组,简单说就是拿到对象的所有键
  • isStrictComparable:检查值是否可以使用严格相等比较(===),主要判断基本类型值和非 NaN 值

源码实现

javascript 复制代码
function getMatchData(object) {
  var result = keys(object),
    length = result.length;

  while (length--) {
    var key = result[length],
      value = object[key];

    result[length] = [key, value, isStrictComparable(value)];
  }
  return result;
}

实现思路

getMatchData 的实现非常直接:先用 keys 函数拿到对象的所有键,然后从后往前遍历这些键(用 while (length--))。对于每个键,它都:

  1. 获取这个键对应的值
  2. 将数组中当前位置的元素(原本是键名)替换成一个新的三元素数组
  3. 这个三元素数组包含:[键名, 键值, 是否可用严格比较]

最后返回转换好的数组。整个过程就是把对象的结构重新组织,便于后续的匹配操作。

源码解析

初始化

javascript 复制代码
var result = keys(object),
  length = result.length;

首先获取对象的所有键,存在 result 变量中。比如对于对象 {a: 1, b: "hello"}result 就是 ["a", "b"]

keys 函数会根据对象类型选择合适的方法获取键:

  • 对于普通对象,使用 Object.keys 或其兼容实现
  • 对于数组类对象,会有特殊处理

转换键值对

javascript 复制代码
while (length--) {
  var key = result[length],
    value = object[key];

  result[length] = [key, value, isStrictComparable(value)];
}

这里从后往前遍历键数组,为什么要倒着遍历呢?这是一种常见的优化手段,倒序遍历通常比正序遍历要快一点。

对于每个键,函数都做了三件事:

  1. 获取当前键名 key(比如 "a")
  2. 获取这个键对应的值 value(比如 1)
  3. 用一个三元素数组 [key, value, isStrictComparable(value)] 替换原数组中的键名

最关键的是第三个元素:isStrictComparable(value) 的结果,这是一个布尔值,告诉我们这个值是否可以用严格相等(===)来比较。

isStrictComparable 的作用

javascript 复制代码
function isStrictComparable(value) {
  return value === value && !isObject(value);
}

这个函数判断一个值是否满足两个条件:

  1. value === value:排除 NaN,因为 NaN !== NaN
  2. !isObject(value):值不是对象(基本类型值可以直接用 === 比较)

基本类型值(数字、字符串、布尔值等)和非 NaN 值都会返回 true,而对象、数组和 NaN 会返回 false

javascript 复制代码
isStrictComparable(1); // true
isStrictComparable("string"); // true
isStrictComparable(true); // true
isStrictComparable({}); // false
isStrictComparable([]); // false
isStrictComparable(NaN); // false

为什么需要这个标识?

这个标识是 Lodash 匹配系统中的关键部分,它在 baseIsMatch 函数中被用于优化匹配性能。以下是它的实际用途:

  1. 优化匹配路径 :在 baseIsMatch 中,它会先检查这个标识。如果值是可以严格比较的(标识为 true),就直接用 === 比较,这是最快的方式。

    javascript 复制代码
    // baseIsMatch 中的代码片段
    if (
      noCustomizer && data[2] // data[2] 就是 isStrictComparable(value) 的结果
        ? data[1] !== object[data[0]]
        : !(data[0] in object)
    ) {
      return false;
    }
  2. 避免不必要的深度比较 :对于简单值,不需要调用复杂的 baseIsEqual 函数进行深度比较,直接用 === 就够了,这大大提高了性能。

  3. 处理不同类型的值:对于对象和数组,Lodash 会使用更复杂的比较方法;对于基本类型,则使用更简单快速的方法。

实际效果举例:

javascript 复制代码
// 检查对象是否匹配模式
_.isMatch({ a: 1, b: { c: 3 } }, { a: 1 }); // true

// 内部执行过程(简化):
// 1. getMatchData({ a: 1 }) 返回 [["a", 1, true]]
// 2. baseIsMatch 看到第三个元素是 true,就直接用 === 比较 1 === 1
// 3. 比较成功,返回 true

返回结果

javascript 复制代码
return result;

函数最后返回转换后的数组。例如,对于输入对象 { a: 1, b: 'string' },返回的结果是:

javascript 复制代码
[
  ["a", 1, true],
  ["b", "string", true],
];

这种数据结构让 Lodash 的匹配算法能够:

  1. 快速获取键名和键值(不用再查询原对象)
  2. 根据第三个元素决定用什么比较策略(严格比较还是深度比较)

总结

getMatchData 虽然看起来简单,但它在 Lodash 内部匹配系统中扮演着关键角色。它的设计体现了几个重要的编程思想:

  1. 数据转换:将对象转换为更适合特定操作的格式,这是函数式编程中的常见模式。

  2. 性能优化

    • 通过标记值的比较方式来避免不必要的复杂比较
    • 使用倒序遍历优化循环性能
    • 在原数组上直接修改,避免创建新数组的开销
  3. 关注点分离:将数据提取与匹配逻辑分开,使代码更清晰。

  4. 预计算:提前计算和标记值的比较方式,避免在匹配时重复判断。

这种设计方法不仅提高了代码性能,也使得代码更易于理解和维护。即使是一个小小的内部工具函数,也能从中学到很多优秀的代码设计思想。

相关推荐
WeiXiao_Hyy14 分钟前
成为 Top 1% 的工程师
java·开发语言·javascript·经验分享·后端
吃杠碰小鸡31 分钟前
高中数学-数列-导数证明
前端·数学·算法
kingwebo'sZone37 分钟前
C#使用Aspose.Words把 word转成图片
前端·c#·word
xjt_09011 小时前
基于 Vue 3 构建企业级 Web Components 组件库
前端·javascript·vue.js
我是伪码农1 小时前
Vue 2.3
前端·javascript·vue.js
夜郎king2 小时前
HTML5 SVG 实现日出日落动画与实时天气可视化
前端·html5·svg 日出日落
辰风沐阳2 小时前
JavaScript 的宏任务和微任务
javascript
夏幻灵3 小时前
HTML5里最常用的十大标签
前端·html·html5
冰暮流星3 小时前
javascript之二重循环练习
开发语言·javascript·数据库
Mr Xu_3 小时前
Vue 3 中 watch 的使用详解:监听响应式数据变化的利器
前端·javascript·vue.js