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 的区别

assignValuebaseAssignValue 的主要区别在于:

  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 本身很简洁,但它体现了优秀的函数设计原则:单一职责、关注性能、处理边界情况。

相关推荐
GIS之路几秒前
GeoTools 读取影像元数据
前端
ssshooter29 分钟前
VSCode 自带的 TS 版本可能跟项目TS 版本不一样
前端·面试·typescript
你的人类朋友34 分钟前
【Node.js】什么是Node.js
javascript·后端·node.js
Jerry1 小时前
Jetpack Compose 中的状态
前端
dae bal2 小时前
关于RSA和AES加密
前端·vue.js
柳杉2 小时前
使用three.js搭建3d隧道监测-2
前端·javascript·数据可视化
lynn8570_blog2 小时前
低端设备加载webp ANR
前端·算法
LKAI.3 小时前
传统方式部署(RuoYi-Cloud)微服务
java·linux·前端·后端·微服务·node.js·ruoyi
刺客-Andy3 小时前
React 第七十节 Router中matchRoutes的使用详解及注意事项
前端·javascript·react.js
前端工作日常3 小时前
我对eslint的进一步学习
前端·eslint