Lodash源码阅读-baseGet

Lodash 源码阅读-baseGet

概述

baseGet 是 Lodash 内部的一个工具函数,用于根据指定的路径从对象中获取值。它是 _.get 方法的基础实现,支持使用点号路径(如 'a.b.c')或数组路径(如 ['a', 'b', 'c'])来访问嵌套对象的属性,是 Lodash 中实现对象属性访问功能的核心。

前置学习

依赖函数

  • castPath:将路径转换为标准格式(数组形式),处理字符串路径和数组路径
  • toKey:将路径片段转换为有效的属性键,处理字符串、数字、Symbol 等不同类型的键

技术知识

  • 路径解析:将字符串路径解析为路径片段数组
  • 对象属性访问:使用方括号语法 object[key] 动态访问对象属性
  • 空值处理:处理访问路径中的 nullundefined
  • 短路求值:在遇到无效路径时提前返回
  • 循环遍历:使用 while 循环逐层访问嵌套对象

源码实现

javascript 复制代码
function baseGet(object, path) {
  path = castPath(path, object);

  var index = 0,
    length = path.length;

  while (object != null && index < length) {
    object = object[toKey(path[index++])];
  }
  return index && index == length ? object : undefined;
}

实现思路

baseGet 函数的实现思路非常直接:

  1. 首先使用 castPath 将路径转换为标准的数组格式,无论输入的是字符串路径还是数组路径。

  2. 然后使用 while 循环逐层访问对象的嵌套属性:

    • 只要当前对象不是 nullundefined,且还有路径片段未处理,就继续循环
    • 在每次循环中,使用 toKey 将当前路径片段转换为有效的属性键,然后访问对象的对应属性
    • 将获取到的属性值作为新的当前对象,继续处理下一个路径片段
  3. 最后,检查是否成功遍历了整个路径:

    • 如果 index 大于 0(表示至少处理了一个路径片段)且等于路径长度(表示处理了所有路径片段),则返回最终获取到的值
    • 否则,返回 undefined,表示路径无效或中途遇到了 nullundefined

这种实现既简洁又高效,能够处理各种复杂的嵌套对象访问场景。

源码解析

路径标准化

javascript 复制代码
path = castPath(path, object);

函数首先调用 castPath(path, object) 将路径转换为标准的数组格式。castPath 函数的作用是:

  • 如果路径已经是数组,则直接返回
  • 如果路径是简单属性键(通过 isKey 判断),则将其包装为单元素数组
  • 否则,将字符串路径解析为路径片段数组(通过 stringToPath

例如:

javascript 复制代码
castPath("a.b.c", obj); // 返回 ['a', 'b', 'c']
castPath(["a", "b", "c"], obj); // 返回 ['a', 'b', 'c']
castPath("a", obj); // 如果 'a' 是简单键,返回 ['a']

初始化变量

javascript 复制代码
var index = 0,
  length = path.length;

这里初始化了两个变量:

  • index:当前处理的路径片段索引,初始为 0
  • length:路径片段的总数

逐层访问属性

javascript 复制代码
while (object != null && index < length) {
  object = object[toKey(path[index++])];
}

这个 while 循环是函数的核心,它逐层访问对象的嵌套属性:

  1. 循环条件 object != null && index < length 确保:

    • 当前对象不是 nullundefinedobject != null 等价于 object !== null && object !== undefined
    • 还有路径片段未处理(index < length
  2. 循环体 object = object[toKey(path[index++])] 执行以下操作:

    • 获取当前路径片段 path[index]
    • 使用 toKey 将路径片段转换为有效的属性键
    • 访问对象的对应属性 object[key]
    • 将获取到的属性值赋给 object,作为下一次循环的当前对象
    • 递增 index,准备处理下一个路径片段

toKey 函数的作用是将路径片段转换为有效的属性键:

  • 如果是字符串或 Symbol,则直接返回
  • 否则,将其转换为字符串
  • 特殊处理 -0,确保 -00 被视为不同的键

例如:

javascript 复制代码
// 对于路径 'a.b.c' 和对象 { a: { b: { c: 42 } } }
// 第一次循环:object = { a: { b: { c: 42 } } }['a'] = { b: { c: 42 } }
// 第二次循环:object = { b: { c: 42 } }['b'] = { c: 42 }
// 第三次循环:object = { c: 42 }['c'] = 42
// 循环结束,object = 42

返回结果

javascript 复制代码
return index && index == length ? object : undefined;

这行代码决定函数的返回值:

  • 如果 index 大于 0(index 为真)且等于路径长度(index == length),表示成功遍历了整个路径,则返回最终获取到的值 object
  • 否则,返回 undefined,表示路径无效或中途遇到了 nullundefined

这个条件检查确保了函数的行为符合预期:

  • 如果对象为 nullundefined,返回 undefined
  • 如果路径中的某个中间属性为 nullundefined,返回 undefined
  • 如果成功遍历了整个路径,返回找到的值,即使该值本身是 nullundefined

例如:

javascript 复制代码
// 对于 object = { a: { b: null } } 和 path = 'a.b.c'
// 第一次循环后:object = { b: null }
// 第二次循环后:object = null
// 第三次循环不会执行,因为 object 为 null
// index = 2, length = 3,所以返回 undefined

总结

baseGet 函数是 Lodash 中对象属性访问功能的核心实现,它通过巧妙的设计实现了安全、灵活的嵌套属性访问。其设计体现了几个重要的软件工程原则:

  1. 健壮性

    • 安全处理 nullundefined 值,避免运行时错误
    • 对无效路径返回 undefined 而不是抛出异常
    • 支持各种类型的路径表示(字符串、数组)
  2. 灵活性

    • 支持点号路径(如 'a.b.c')和数组路径(如 ['a', 'b', 'c']
    • 通过 toKey 支持各种类型的属性键(字符串、数字、Symbol)
    • _.get 结合提供默认值功能
  3. 性能优化

    • 使用 while 循环而不是递归,避免函数调用开销
    • 通过 castPath 缓存路径解析结果,避免重复解析
    • 在遇到无效路径时提前返回,避免不必要的计算
  4. 可组合性

    • 作为基础函数被其他高级函数(如 _.get_.has_.set)复用
    • 与路径处理函数(如 castPathtoKey)协同工作
    • 遵循单一职责原则,专注于属性访问逻辑

这些设计思想不仅适用于对象属性访问,也可以应用到其他需要处理嵌套数据结构的场景中,是处理复杂数据的重要参考。

相关推荐
还是鼠鼠1 小时前
Node.js自定义中间件
javascript·vscode·中间件·node.js·json·express
大莲芒4 小时前
react 15-16-17-18各版本的核心区别、底层原理及演进逻辑的深度解析--react17
前端·react.js·前端框架
木木黄木木6 小时前
html5炫酷3D文字效果项目开发实践
前端·3d·html5
Li_Ning216 小时前
【接口重复请求】axios通过AbortController解决页面切换过快,接口重复请求问题
前端
胡八一7 小时前
Window调试 ios 的 Safari 浏览器
前端·ios·safari
Dontla7 小时前
前端页面鼠标移动监控(鼠标运动、鼠标监控)鼠标节流处理、throttle、限制触发频率(setTimeout、clearInterval)
前端·javascript
再学一点就睡7 小时前
深拷贝与浅拷贝:代码世界里的永恒与瞬间
前端·javascript
CrimsonHu7 小时前
B站首页的 Banner 这么好看,我用原生 JS + 三大框架统统给你复刻一遍!
前端·javascript·css
Enti7c8 小时前
前端表单输入框验证
前端·javascript·jquery
拉不动的猪8 小时前
几种比较实用的指令举例
前端·javascript·面试