Lodash源码阅读-zipObjectDeep

83 阅读4分钟

Lodash 源码阅读-zipObjectDeep

概述

zipObjectDeep是 Lodash 中的一个数组方法,它接收两个数组作为参数,一个是属性路径数组,另一个是对应的值数组,然后创建一个对象,将属性路径和值一一对应组合。与zipObject不同,zipObjectDeep支持嵌套路径,能够根据属性路径字符串(如'a.b.c''a[0].b')创建嵌套对象和数组。

前置学习

依赖函数

  • baseZipObjectzipObjectzipObjectDeep的底层实现,它接收属性数组、值数组和赋值函数,返回组装好的对象
  • baseSet:用于设置对象深层属性值的函数,支持处理路径字符串并创建嵌套结构

技术知识

  • 属性路径:JavaScript 中访问和设置嵌套对象属性的路径表示方式
  • 数组和对象的操作:遍历、赋值、创建嵌套结构
  • 类型判断:确保输入参数有效性的处理

源码实现

function zipObjectDeep(props, values) {
  return baseZipObject(props || [], values || [], baseSet);
}

实现思路

zipObjectDeep的实现非常简洁,主要依赖于两个核心函数:

  1. 使用baseZipObject作为基础实现,处理属性和值的配对
  2. 通过传入baseSet作为赋值函数,实现深层属性的设置和嵌套对象的创建
  3. 为输入参数提供默认空数组,增强函数的健壮性

这种设计让函数能够处理复杂的对象构建需求,特别是在需要基于路径创建多层嵌套结构时。

源码解析

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 的实现与工作原理

baseZipObjectzipObjectzipObjectDeep的内部实现:

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函数的工作流程:

  1. 创建空结果对象result
  2. 遍历props数组的每个属性路径
  3. 获取对应索引的值,如果值数组长度不足则使用undefined
  4. 使用传入的assignFunc将值分配给结果对象中的相应属性
  5. 返回构建好的结果对象

zipObjectDeep通过传入baseSet作为assignFunc,赋予了处理嵌套路径的能力,而普通的zipObject则使用assignValue函数,只能处理直接属性。

3. baseSet 的实现与路径处理

baseSet是实现深层属性设置的关键:

  1. 处理属性路径:使用castPath将字符串路径转为数组格式
  2. 安全性保护:防止修改__proto__constructorprototype属性,避免原型污染
  3. 智能创建嵌套结构:
    • 如果路径中有数字索引,创建数组
    • 否则创建普通对象
  4. 维护现有结构:如果中间节点已存在,保留其结构
  5. 深层赋值:逐层进入嵌套结构,直到到达最终位置

这种实现使得zipObjectDeep能够处理各种复杂的属性路径,如:

_.zipObjectDeep(["a.b[0].c", "a.b[1].d"], [1, 2]);
// 结果: { 'a': { 'b': [{ 'c': 1 }, { 'd': 2 }] } }

在这个例子中,baseSet会根据'a.b[0].c'创建嵌套对象和数组结构,并在正确的位置设置值。

总结

  1. 声明式对象构建zipObjectDeep提供了一种声明式方法来构建复杂的嵌套对象结构,无需手动创建每一层。

  2. 路径驱动的数据结构:利用属性路径字符串自动创建正确的嵌套结构,包括对象和数组。

  3. 函数组合:通过组合使用核心函数(baseZipObjectbaseSet)实现复杂功能,展示了良好的模块化设计。

  4. 防御性编程:代码中包含防止原型污染的保护措施,增强了安全性。