Lodash 源码阅读-baseGet
概述
baseGet
是 Lodash 内部的一个工具函数,用于根据指定的路径从对象中获取值。它是 _.get
方法的基础实现,支持使用点号路径(如 'a.b.c'
)或数组路径(如 ['a', 'b', 'c']
)来访问嵌套对象的属性,是 Lodash 中实现对象属性访问功能的核心。
前置学习
依赖函数
castPath
:将路径转换为标准格式(数组形式),处理字符串路径和数组路径toKey
:将路径片段转换为有效的属性键,处理字符串、数字、Symbol 等不同类型的键
技术知识
- 路径解析:将字符串路径解析为路径片段数组
- 对象属性访问:使用方括号语法
object[key]
动态访问对象属性 - 空值处理:处理访问路径中的
null
或undefined
值 - 短路求值:在遇到无效路径时提前返回
- 循环遍历:使用
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
函数的实现思路非常直接:
-
首先使用
castPath
将路径转换为标准的数组格式,无论输入的是字符串路径还是数组路径。 -
然后使用
while
循环逐层访问对象的嵌套属性:- 只要当前对象不是
null
或undefined
,且还有路径片段未处理,就继续循环 - 在每次循环中,使用
toKey
将当前路径片段转换为有效的属性键,然后访问对象的对应属性 - 将获取到的属性值作为新的当前对象,继续处理下一个路径片段
- 只要当前对象不是
-
最后,检查是否成功遍历了整个路径:
- 如果
index
大于 0(表示至少处理了一个路径片段)且等于路径长度(表示处理了所有路径片段),则返回最终获取到的值 - 否则,返回
undefined
,表示路径无效或中途遇到了null
或undefined
- 如果
这种实现既简洁又高效,能够处理各种复杂的嵌套对象访问场景。
源码解析
路径标准化
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
:当前处理的路径片段索引,初始为 0length
:路径片段的总数
逐层访问属性
javascript
while (object != null && index < length) {
object = object[toKey(path[index++])];
}
这个 while
循环是函数的核心,它逐层访问对象的嵌套属性:
-
循环条件
object != null && index < length
确保:- 当前对象不是
null
或undefined
(object != null
等价于object !== null && object !== undefined
) - 还有路径片段未处理(
index < length
)
- 当前对象不是
-
循环体
object = object[toKey(path[index++])]
执行以下操作:- 获取当前路径片段
path[index]
- 使用
toKey
将路径片段转换为有效的属性键 - 访问对象的对应属性
object[key]
- 将获取到的属性值赋给
object
,作为下一次循环的当前对象 - 递增
index
,准备处理下一个路径片段
- 获取当前路径片段
toKey
函数的作用是将路径片段转换为有效的属性键:
- 如果是字符串或 Symbol,则直接返回
- 否则,将其转换为字符串
- 特殊处理
-0
,确保-0
和0
被视为不同的键
例如:
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
,表示路径无效或中途遇到了null
或undefined
这个条件检查确保了函数的行为符合预期:
- 如果对象为
null
或undefined
,返回undefined
- 如果路径中的某个中间属性为
null
或undefined
,返回undefined
- 如果成功遍历了整个路径,返回找到的值,即使该值本身是
null
或undefined
例如:
javascript
// 对于 object = { a: { b: null } } 和 path = 'a.b.c'
// 第一次循环后:object = { b: null }
// 第二次循环后:object = null
// 第三次循环不会执行,因为 object 为 null
// index = 2, length = 3,所以返回 undefined
总结
baseGet
函数是 Lodash 中对象属性访问功能的核心实现,它通过巧妙的设计实现了安全、灵活的嵌套属性访问。其设计体现了几个重要的软件工程原则:
-
健壮性:
- 安全处理
null
和undefined
值,避免运行时错误 - 对无效路径返回
undefined
而不是抛出异常 - 支持各种类型的路径表示(字符串、数组)
- 安全处理
-
灵活性:
- 支持点号路径(如
'a.b.c'
)和数组路径(如['a', 'b', 'c']
) - 通过
toKey
支持各种类型的属性键(字符串、数字、Symbol) - 与
_.get
结合提供默认值功能
- 支持点号路径(如
-
性能优化:
- 使用
while
循环而不是递归,避免函数调用开销 - 通过
castPath
缓存路径解析结果,避免重复解析 - 在遇到无效路径时提前返回,避免不必要的计算
- 使用
-
可组合性:
- 作为基础函数被其他高级函数(如
_.get
、_.has
、_.set
)复用 - 与路径处理函数(如
castPath
、toKey
)协同工作 - 遵循单一职责原则,专注于属性访问逻辑
- 作为基础函数被其他高级函数(如
这些设计思想不仅适用于对象属性访问,也可以应用到其他需要处理嵌套数据结构的场景中,是处理复杂数据的重要参考。