Lodash源码阅读-assignValue

Lodash 源码阅读-assignValue

功能概述

assignValue 是 Lodash 中的一个内部工具函数,主要负责智能地将值赋给对象的属性。它与直接赋值不同的是,它会先检查当前对象是否已经有相同的值,如果有则跳过赋值操作,只有在值不同或属性不存在时才执行赋值。这种优化可以避免不必要的属性操作,提高性能并防止触发不必要的副作用。

这个函数在 Lodash 的很多核心功能中被使用,比如 _.assign、_.merge 等对象操作方法。

前置学习

依赖函数

  • baseAssignValue:底层的属性赋值实现,不进行相等性检查
  • hasOwnProperty:检查对象自身是否含有指定属性
  • eq:使用 SameValueZero 算法比较两个值是否相等

技术知识

  • JavaScript 对象属性操作:包括属性访问、赋值、检查等
  • 相等性比较:JavaScript 中的松散相等(==)、严格相等(===)和 SameValueZero 比较
  • 属性描述符:了解属性的特性(可写、可枚举等)

源码实现

javascript 复制代码
function assignValue(object, key, value) {
  var objValue = object[key];
  if (
    !(hasOwnProperty.call(object, key) && eq(objValue, value)) ||
    (value === undefined && !(key in object))
  ) {
    baseAssignValue(object, key, value);
  }
}

实现思路

assignValue 的实现思路可归纳为:

  1. 首先获取对象现有的属性值 objValue = object[key]
  2. 然后检查是否需要进行赋值,满足以下任一条件时才执行赋值操作:
    • 对象没有该属性(!hasOwnProperty.call(object, key)),或者
    • 对象有该属性但值不相等(!eq(objValue, value)),或者
    • 要赋的值是 undefined 且对象上不存在该属性(value === undefined && !(key in object))
  3. 如果需要赋值,则调用 baseAssignValue 函数执行实际的赋值操作

这种实现可以避免不必要的属性重新赋值,特别是在处理大量属性或复杂对象时能提高性能。

源码解析

让我们逐行分析 assignValue 函数的实现:

javascript 复制代码
function assignValue(object, key, value) {

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

  • object:要修改的目标对象
  • key:要设置的属性名
  • value:要赋的值
javascript 复制代码
var objValue = object[key];

获取对象上已有的属性值。这一步很重要,因为后续需要比较这个值和要赋的新值是否相等。

javascript 复制代码
  if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) ||
      (value === undefined && !(key in object))) {

这个条件判断是 assignValue 的核心,由两部分组成:

  1. !(hasOwnProperty.call(object, key) && eq(objValue, value)):

    • hasOwnProperty.call(object, key):检查对象自身是否有该属性(不包括原型链上的属性)
    • eq(objValue, value):使用 eq 函数比较属性的当前值与要赋的值是否相等
    • 整体条件的含义是:如果对象没有该属性,或者对象有该属性但值与要赋的值不同,则需要赋值
  2. (value === undefined && !(key in object)):

    • 这是一个特殊情况的处理:如果要赋的值是 undefined,且对象上不存在该属性(包括自身和原型链),才进行赋值
    • 处理这个特殊情况是因为,对于不存在的属性,访问它会返回 undefined,但这不意味着该属性设置了 undefined 值

这个条件设计得很巧妙,既避免了不必要的属性赋值,又处理了特殊情况。

javascript 复制代码
    baseAssignValue(object, key, value);
  }
}

如果满足条件,则调用 baseAssignValue 执行实际的赋值操作。baseAssignValue 负责处理最终的属性赋值,包括对 __proto__ 属性的特殊处理。

与 baseAssignValue 的区别

assignValue 和 baseAssignValue 的主要区别在于:

  1. 相等性检查 :assignValue 会先检查对象是否已有相同值的属性,只有在需要时才赋值;而 baseAssignValue 直接执行赋值操作,不进行任何检查
  2. 调用关系 :assignValue 是一个高级包装函数,而 baseAssignValue 是底层实现
  3. 性能优化 :assignValue 的检查逻辑有助于避免不必要的属性重新定义,特别是当处理可能有副作用的属性时(如使用 getter/setter 定义的属性)

性能考量

assignValue 的实现包含了几个重要的性能优化:

  1. 避免不必要的赋值操作:通过先检查值是否已经相等,可以避免重复赋值相同的值
  2. 减少副作用:在使用 getter/setter 定义的属性上,避免不必要的 setter 调用很重要
  3. 高效的相等性比较 :使用 eq 函数实现高效的相等性比较,包括对 NaN 的正确处理

这些优化在处理大型对象或频繁的属性更新操作时尤为重要。

总结

assignValue 是 Lodash 中一个内部工具函数,它通过智能地判断是否需要进行属性赋值,提高了性能并减少了不必要的操作。这个函数展示了几个重要的编程思想:

  1. 效率优先:只在必要时执行操作,避免重复工作
  2. 边界情况处理 :正确处理 undefined 值和对象属性不存在的情况
  3. 分层设计 :高级函数 assignValue 负责判断逻辑,底层函数 baseAssignValue 负责实际执行
  4. 正确的相等性比较:使用符合规范的 SameValueZero 比较算法

虽然 assignValue 本身很简洁,但它体现了优秀的函数设计原则:单一职责、关注性能、处理边界情况。

相关推荐
传人once42 分钟前
悬停旋转放大和位移效果如何写
javascript·css·动画
库拉镜像AI牛牛1 小时前
短剧内容自动化生产:知漫剧工作室落地教程
大数据·服务器·前端·人工智能·语音识别
律宏阔1 小时前
Flutter 列表图片内存优化:CachedNetworkImage 与 ClipRRect 的取舍
前端·flutter
行者全栈架构师1 小时前
【鸿蒙心迹】相册文搜图工程化落地:HarmonyOS 7.0 端侧 AI 检索全链路实战与 5 个踩坑解法
前端
律宏阔1 小时前
Dart 3 Record:解决 Future.wait 异构返回值的类型问题
前端·flutter
Seraphina361 小时前
DVWA(XSS Reflected/Stored High,CSRF Low)
前端·xss·csrf
默_笙2 小时前
🍔 中间件不只是打日志:四个钩子、一次短路,和自带的工具
前端·javascript
程序员老赵3 小时前
Docker 部署 Dolibarr:轻松搭建开源 ERP/CRM 平台
运维·前端·后端
星禾元亨3 小时前
同一件事有三份资料,AI 该信哪一份?冲突判定、优先级链与处置流程
前端·人工智能
Dovis(誓平步青云)3 小时前
家里设备越来越多,如何用一张空间地图控制灯光和温度![
android·java·前端·javascript·人工智能·电脑