Lodash源码阅读-baseMatchesProperty

Lodash 源码阅读-baseMatchesProperty

概述

baseMatchesProperty 是 Lodash 内部的工具函数,用来创建一个检查函数,这个函数会判断对象指定路径上的值是否与给定的源值相等。它是 _.matchesProperty 方法的核心实现,常用于集合操作中创建属性匹配断言函数。

前置学习

依赖函数

  • isKey:检查值是否为简单属性路径
  • isStrictComparable:检查值是否可以使用严格相等(===)比较
  • matchesStrictComparable:创建使用严格相等比较的匹配函数
  • toKey:将值转换为属性路径的键
  • get:获取对象指定路径上的值
  • hasIn:检查路径是否存在于对象中
  • baseIsEqual:深度比较两个值是否相等

技术知识

  • 高阶函数:返回函数的函数
  • 属性路径:表示嵌套对象属性的访问路径
  • 闭包:函数记住并访问其词法作用域
  • 位掩码:使用位运算符组合多个标志
  • 性能优化:通过快速路径提高函数执行效率

源码实现

javascript 复制代码
function baseMatchesProperty(path, srcValue) {
  if (isKey(path) && isStrictComparable(srcValue)) {
    return matchesStrictComparable(toKey(path), srcValue);
  }
  return function (object) {
    var objValue = get(object, path);
    return objValue === undefined && objValue === srcValue
      ? hasIn(object, path)
      : baseIsEqual(
          srcValue,
          objValue,
          COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG
        );
  };
}

实现思路

baseMatchesProperty 根据路径和源值的特性,采用不同策略创建匹配函数:

  1. 当路径是简单属性键且源值可以使用严格相等比较时,采用简单高效的 matchesStrictComparable 函数

  2. 否则,创建一个更复杂的匹配函数,这个函数会:

    • 获取对象指定路径上的值
    • 处理特殊情况(如 undefined 值)
    • 使用深度比较判断属性值是否匹配

这种分层设计既保证了比较的准确性,又通过快速路径优化了简单场景的性能。

源码解析

快速路径优化

javascript 复制代码
if (isKey(path) && isStrictComparable(srcValue)) {
  return matchesStrictComparable(toKey(path), srcValue);
}

这段代码是性能优化,当满足两个条件时使用简单高效的比较方式:

  1. path 是简单属性键(通过 isKey 判断)

    简单属性键是像 'name' 这样的直接属性,而不是 'user.profile.name'['user', 'profile', 'name'] 这样的嵌套路径

  2. srcValue 可以使用严格相等比较(通过 isStrictComparable 判断)

    可严格比较的值包括基本类型(字符串、数字、布尔值等)和非对象值

以下是 isStrictComparable 的实现:

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

这个函数检查值是否可以使用 === 操作符安全比较,它排除了 NaN(因为 NaN !== NaN)和对象(需要深度比较)。

当条件满足时,matchesStrictComparable 会创建一个简单函数:

javascript 复制代码
function matchesStrictComparable(key, srcValue) {
  return function (object) {
    if (object == null) {
      return false;
    }
    return (
      object[key] === srcValue &&
      (srcValue !== undefined || key in Object(object))
    );
  };
}

这个函数直接使用 === 比较,避免了更复杂的深度比较操作。

创建复杂匹配函数

javascript 复制代码
return function (object) {
  var objValue = get(object, path);
  return objValue === undefined && objValue === srcValue
    ? hasIn(object, path)
    : baseIsEqual(
        srcValue,
        objValue,
        COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG
      );
};

当不满足快速路径条件时,函数返回一个更复杂的匹配函数:

  1. 使用 get(object, path) 获取对象指定路径上的值

    javascript 复制代码
    function get(object, path, defaultValue) {
      var result = object == null ? undefined : baseGet(object, path);
      return result === undefined ? defaultValue : result;
    }

    这支持深层嵌套路径访问,如 'user.profile.name'['user', 'profile', 'name']

  2. 处理特殊情况:对象值和源值都是 undefined

    javascript 复制代码
    objValue === undefined && objValue === srcValue;

    这种情况下,需要区分"路径不存在"和"路径存在但值为 undefined"

    通过 hasIn(object, path) 检查路径是否存在于对象中:

    javascript 复制代码
    function hasIn(object, path) {
      return object != null && hasPath(object, path, baseHasIn);
    }
  3. 对于其他情况,使用 baseIsEqual 进行深度比较

    javascript 复制代码
    baseIsEqual(
      srcValue,
      objValue,
      COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG
    );

    使用的位掩码标志:

    • COMPARE_PARTIAL_FLAG(值为 1):启用部分比较模式
    • COMPARE_UNORDERED_FLAG(值为 2):启用无序比较模式

    这允许比较复杂对象和数组,即使它们的属性顺序不同

实际案例:

javascript 复制代码
// 创建一个检查函数,判断对象的 'user.type' 是否为 'admin'
const isAdmin = baseMatchesProperty("user.type", "admin");

// 使用该函数检查对象
isAdmin({ user: { type: "admin", name: "John" } }); // 返回 true
isAdmin({ user: { type: "user", name: "Jane" } }); // 返回 false

对于数组路径和复杂对象比较:

javascript 复制代码
// 检查对象的 user.hobbies 数组是否包含 ['reading', 'coding']
const hasHobbies = baseMatchesProperty(
  ["user", "hobbies"],
  ["reading", "coding"]
);

// 由于使用 COMPARE_UNORDERED_FLAG,数组元素顺序不影响结果
hasHobbies({ user: { hobbies: ["coding", "reading"] } }); // 返回 true

总结

baseMatchesProperty 是 Lodash 中属性值匹配功能的核心实现,它有几个显著特点:

  1. 分层设计:根据输入参数特性选择不同实现策略,简单情况用快速方法,复杂情况用完整比较

  2. 性能优化:通过快速路径处理常见简单情况,减少不必要的复杂比较

  3. 完备性:处理了各种边缘情况,如 undefined 值和不存在的路径

  4. 灵活性:支持简单属性和复杂嵌套路径,同时支持基本值比较和深度对象比较

baseMatchesProperty 的典型应用场景包括:

  • 在集合操作(如 _.find_.filter)中作为断言函数
  • 实现数据查询和过滤条件
  • 创建基于属性值的数据验证器
  • 在函数式编程中组合构建复杂条件

这种实现方式体现了函数式编程的强大之处:高阶函数和闭包的结合使我们可以动态生成专用的比较函数,既保证了灵活性又维持了高性能。

相关推荐
A_aspectJ9 分钟前
【Bootstrap V4系列】学习入门教程之 组件-输入组(Input group)
前端·css·学习·bootstrap·html
兆。22 分钟前
电子商城后台管理平台-Flask Vue项目开发
前端·vue.js·后端·python·flask
互联网搬砖老肖32 分钟前
Web 架构之负载均衡全解析
前端·架构·负载均衡
sunbyte1 小时前
Tailwind CSS v4 主题化实践入门(自定义 Theme + 主题模式切换)✨
前端·javascript·css·tailwindcss
风之舞_yjf2 小时前
Vue基础(8)_监视属性、深度监视、监视的简写形式
javascript·vue.js·ecmascript
湛海不过深蓝2 小时前
【css】css统一设置变量
前端·css
DONSEE广东东信智能读卡器2 小时前
蓝牙身份证阅读器使用Uniapp调用二次开发demo
javascript·uni-app·蓝牙·身份证阅读器
Codingwiz_Joy2 小时前
Day28 -js开发01 -JS三个实例:文件上传 & 登录验证 & 购物商城 & ---逻辑漏洞复现 及 判断js的payload思路
开发语言·javascript·安全·安全性测试
程序员的世界你不懂3 小时前
tomcat6性能优化
前端·性能优化·firefox
爱吃巧克力的程序媛3 小时前
QML ProgressBar控件详解
前端