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. 预计算:提前计算和标记值的比较方式,避免在匹配时重复判断。

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

相关推荐
写代码的小王吧28 分钟前
【安全】Web渗透测试(全流程)_渗透测试学习流程图
linux·前端·网络·学习·安全·网络安全·ssh
小小小小宇1 小时前
CSS 渐变色
前端
snow@li1 小时前
前端:开源软件镜像站 / 清华大学开源软件镜像站 / 阿里云 / 网易 / 搜狐
前端·开源软件镜像站
小小小小宇2 小时前
配置 Gemini Code Assist 插件
前端
one 大白(●—●)2 小时前
前端用用jsonp的方式解决跨域问题
前端·jsonp跨域
刺客-Andy2 小时前
前端加密方式 AES对称加密 RSA非对称加密 以及 MD5哈希算法详解
前端·javascript·算法·哈希算法
记得早睡~2 小时前
leetcode122-买卖股票的最佳时机II
javascript·数据结构·算法·leetcode
前端开发张小七3 小时前
13.Python Socket服务端开发指南
前端·python
前端开发张小七3 小时前
14.Python Socket客户端开发指南
前端·python
ElasticPDF-新国产PDF编辑器3 小时前
Vue 项目 PDF 批注插件库在线版 API 示例教程
前端·vue.js·pdf