Lodash源码阅读-copyObject

Lodash 源码阅读-copyObject

功能概述

copyObject 是 Lodash 内部的一个工具函数,主要用于将源对象的特定属性复制到目标对象上。它支持自定义复制行为,能处理新对象创建和现有对象更新两种情况,是 Lodash 实现对象操作相关方法的基础函数。

前置学习

依赖函数

  • baseAssignValue:底层的属性赋值实现,不检查现有值,直接赋值
  • assignValue:智能属性赋值,会检查属性是否需要更新
  • Object 构造器 :用于在 isNew 为 true 且未提供 object 时创建空对象

技术知识

  • JavaScript 对象遍历:通过索引和长度进行属性遍历
  • 对象属性赋值:了解 JavaScript 中对象属性的赋值机制
  • 函数回调模式:理解如何使用自定义函数(customizer)来控制复制行为
  • 引用类型和值类型:理解 JavaScript 中的值传递和引用传递

源码实现

javascript 复制代码
function copyObject(source, props, object, customizer) {
  var isNew = !object;
  object || (object = {});

  var index = -1,
    length = props.length;

  while (++index < length) {
    var key = props[index];

    var newValue = customizer
      ? customizer(object[key], source[key], key, object, source)
      : undefined;

    if (newValue === undefined) {
      newValue = source[key];
    }
    if (isNew) {
      baseAssignValue(object, key, newValue);
    } else {
      assignValue(object, key, newValue);
    }
  }
  return object;
}

实现思路

copyObject 的实现思路可以归纳为以下几点:

  1. 确定目标对象:如果没有提供目标对象(object),则创建一个新的空对象
  2. 遍历要复制的属性列表(props
  3. 对于每个属性:
    • 如果提供了自定义处理函数(customizer),则使用它计算新值
    • 如果没有提供自定义处理函数或其返回 undefined,则直接使用源对象的属性值
  4. 根据是新建对象还是更新对象,选择不同的赋值方法:
    • 新建对象:使用 baseAssignValue 直接赋值(更高效)
    • 更新对象:使用 assignValue 智能赋值(避免不必要的更新)
  5. 最后返回复制后的对象

这种实现既灵活又高效,能处理多种对象复制场景。

源码解析

让我们逐行解析 copyObject 函数的实现:

javascript 复制代码
function copyObject(source, props, object, customizer) {

函数定义,接收四个参数:

  • source:源对象,要从中复制属性的对象
  • props:要复制的属性列表(数组形式)
  • object:目标对象,要将属性复制到的对象(可选)
  • customizer:自定义处理函数,用于自定义属性复制行为(可选)
javascript 复制代码
var isNew = !object;
object || (object = {});

这两行代码处理目标对象:

  • isNew 标记是否为新对象:如果未提供 object(值为 null、undefined 或其他假值),则 isNew 为 true
  • 如果未提供 object,则创建一个新的空对象作为目标对象
javascript 复制代码
  var index = -1,
      length = props.length;

  while (++index < length) {

设置循环变量,准备遍历属性列表。

javascript 复制代码
var key = props[index];

获取当前要复制的属性名。

javascript 复制代码
var newValue = customizer
  ? customizer(object[key], source[key], key, object, source)
  : undefined;

这行代码处理自定义复制行为:

  • 如果提供了 customizer 函数,则调用它来计算新值
  • 传递给 customizer 函数的参数依次为:目标对象的当前值、源对象的当前值、属性名、目标对象、源对象
  • 如果没有提供 customizer,则 newValueundefined
javascript 复制代码
if (newValue === undefined) {
  newValue = source[key];
}

这是一个巧妙的设计:如果 customizer 返回 undefined(或者没有提供 customizer),则使用源对象的原始值。这样,自定义函数可以选择性地处理某些属性,而对于返回 undefined 的情况,会回退到默认复制行为。

javascript 复制代码
if (isNew) {
  baseAssignValue(object, key, newValue);
} else {
  assignValue(object, key, newValue);
}

根据目标对象是新建的还是已存在的,选择不同的赋值方法:

  • 如果是新对象(isNew 为 true),使用 baseAssignValue 直接赋值,因为新对象上肯定没有现有属性,不需要做额外检查
  • 如果是现有对象,使用 assignValue 进行智能赋值,会先检查是否需要更新(避免不必要的操作)
javascript 复制代码
  }
  return object;
}

完成所有属性的复制后,返回目标对象。

baseAssignValue 和 assignValue 的选择逻辑

copyObject 在赋值时选择了两个不同的函数,这是一个重要的性能优化:

  1. 对于新对象 :使用 baseAssignValue

    • 新对象上肯定没有现有属性,所以不需要进行 assignValue 中的值比较
    • baseAssignValue 是直接赋值,少了条件判断,性能更高
  2. 对于现有对象 :使用 assignValue

    • 现有对象可能已有同名属性,需要先检查是否值相同
    • 如果值相同则跳过赋值,避免不必要的操作(特别是对于有 getter/setter 的属性)

这种区分处理的方式可以在保持功能完整的同时提高性能。

总结

copyObject 是 Lodash 中一个设计精巧的内部工具函数,它通过以下几个特点实现了高效灵活的对象属性复制:

  1. 灵活性:支持自定义处理函数,可以控制每个属性的复制行为
  2. 效率优化:根据目标对象是新建还是已存在,选择最优的赋值方法
  3. 功能完备:能处理多种属性复制场景,是 Lodash 对象操作方法的基础

这个函数体现了几个重要的设计原则:

  1. 单一职责原则:函数只负责对象属性的复制,不关心属性来源和选择逻辑
  2. 开放/封闭原则:通过 customizer 参数扩展功能,而不需要修改基础代码
  3. 性能优化:在保证功能的前提下,针对不同场景使用最优实现
  4. 合成复用:借助 baseAssignValue 和 assignValue 等基础函数构建更复杂的功能
相关推荐
恋猫de小郭1 小时前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅9 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅9 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊9 小时前
jwt介绍
前端