Lodash 源码阅读-zipObjectDeep
概述
zipObjectDeep是 Lodash 中的一个数组方法,它接收两个数组作为参数,一个是属性路径数组,另一个是对应的值数组,然后创建一个对象,将属性路径和值一一对应组合。与zipObject不同,zipObjectDeep支持嵌套路径,能够根据属性路径字符串(如'a.b.c'或'a[0].b')创建嵌套对象和数组。
前置学习
依赖函数
- baseZipObject:
zipObject和zipObjectDeep的底层实现,它接收属性数组、值数组和赋值函数,返回组装好的对象 - baseSet:用于设置对象深层属性值的函数,支持处理路径字符串并创建嵌套结构
技术知识
- 属性路径:JavaScript 中访问和设置嵌套对象属性的路径表示方式
- 数组和对象的操作:遍历、赋值、创建嵌套结构
- 类型判断:确保输入参数有效性的处理
源码实现
function zipObjectDeep(props, values) {
return baseZipObject(props || [], values || [], baseSet);
}
实现思路
zipObjectDeep的实现非常简洁,主要依赖于两个核心函数:
- 使用
baseZipObject作为基础实现,处理属性和值的配对 - 通过传入
baseSet作为赋值函数,实现深层属性的设置和嵌套对象的创建 - 为输入参数提供默认空数组,增强函数的健壮性
这种设计让函数能够处理复杂的对象构建需求,特别是在需要基于路径创建多层嵌套结构时。
源码解析
1. 函数签名与参数处理
function zipObjectDeep(props, values) {
return baseZipObject(props || [], values || [], baseSet);
}
zipObjectDeep函数接收两个参数:
props:属性路径数组,表示要设置的属性位置values:值数组,对应每个属性路径的值
函数通过props || []和values || []处理参数可能为空的情况,确保传递给baseZipObject的始终是数组,提高了函数的健壮性。
示例:
// 有效输入
_.zipObjectDeep(["a.b.c", "a.b.d"], [1, 2]);
// 结果: { 'a': { 'b': { 'c': 1, 'd': 2 } } }
// props为空
_.zipObjectDeep(null, [1, 2]);
// 结果: {}
// values少于props
_.zipObjectDeep(["a.b", "c.d"], [1]);
// 结果: { 'a': { 'b': 1 }, 'c': { 'd': undefined } }
2. baseZipObject 的实现与工作原理
baseZipObject是zipObject和zipObjectDeep的内部实现:
function baseZipObject(props, values, assignFunc) {
var index = -1,
length = props.length,
valsLength = values.length,
result = {};
while (++index < length) {
var value = index < valsLength ? values[index] : undefined;
assignFunc(result, props[index], value);
}
return result;
}
baseZipObject函数的工作流程:
- 创建空结果对象
result - 遍历
props数组的每个属性路径 - 获取对应索引的值,如果值数组长度不足则使用
undefined - 使用传入的
assignFunc将值分配给结果对象中的相应属性 - 返回构建好的结果对象
zipObjectDeep通过传入baseSet作为assignFunc,赋予了处理嵌套路径的能力,而普通的zipObject则使用assignValue函数,只能处理直接属性。
3. baseSet 的实现与路径处理
baseSet是实现深层属性设置的关键:
- 处理属性路径:使用
castPath将字符串路径转为数组格式 - 安全性保护:防止修改
__proto__、constructor和prototype属性,避免原型污染 - 智能创建嵌套结构:
- 如果路径中有数字索引,创建数组
- 否则创建普通对象
- 维护现有结构:如果中间节点已存在,保留其结构
- 深层赋值:逐层进入嵌套结构,直到到达最终位置
这种实现使得zipObjectDeep能够处理各种复杂的属性路径,如:
_.zipObjectDeep(["a.b[0].c", "a.b[1].d"], [1, 2]);
// 结果: { 'a': { 'b': [{ 'c': 1 }, { 'd': 2 }] } }
在这个例子中,baseSet会根据'a.b[0].c'创建嵌套对象和数组结构,并在正确的位置设置值。
总结
-
声明式对象构建:
zipObjectDeep提供了一种声明式方法来构建复杂的嵌套对象结构,无需手动创建每一层。 -
路径驱动的数据结构:利用属性路径字符串自动创建正确的嵌套结构,包括对象和数组。
-
函数组合:通过组合使用核心函数(
baseZipObject和baseSet)实现复杂功能,展示了良好的模块化设计。 -
防御性编程:代码中包含防止原型污染的保护措施,增强了安全性。