es-toolkit 的 assignIn(extend):兼容 Lodash 的继承属性合并函数完全指南
es-toolkit 的 assignInextend兼容 Lodash 的继承属性合并函数完全指南【免费下载链接】es-toolkitA modern JavaScript utility library thats 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkit导读assignIn是 es-toolkit 在compatLodash 兼容模块中提供的一个对象合并函数其核心能力是将一个或多个源对象的自有属性与原型链上的继承属性一并复制到目标对象。本文以官方文档 docs/ja/compat/reference/object/assignIn.md内容与英文版 docs/compat/reference/object/assignIn.md 一致为主体结合 assignIn 源码、keysIn 源码 与 单元测试系统讲解其用法、与Object.assign/assign的差异、底层实现原理与适用场景。读完本文你将掌握在 Lodash 迁移场景中何时使用assignIn、它如何工作以及为什么官方建议优先使用原生Object.assign。适用前提assignIn是 es-toolkit 为兼容 Lodash API 提供的函数需要从es-toolkit/compat入口导入并不包含在默认的es-toolkit主入口中。一、函数概览与官方警告文档开篇即给出一个醒目的警告warning区块这是理解assignIn定位的关键建议使用Object.assign这个assignIn函数由于需要额外处理继承属性的复制以及值比较逻辑运行速度较慢。请改用更快、更现代的Object.assign。也就是说es-toolkit 提供assignIn的目的主要是保持与 Lodash 的 API 兼容性方便旧代码平滑迁移而非作为日常首选方案。其函数签名如下const result assignIn(target, ...sources);功能定义将源对象的所有属性包括继承属性分配给目标对象。项目说明所属模块es-toolkit/compatLodash 兼容层别名extend行为复制源对象的自有属性 原型链继承属性到目标对象返回值修改后的目标对象原地修改返回同一引用官方建议优先使用原生Object.assign二、基础用法详解2.1 基本复制import { assignIn } from es-toolkit/compat; // 基本用法 const target { a: 1, b: 2 }; const source { b: 3, c: 4 }; const result assignIn(target, source); // 结果: { a: 1, b: 3, c: 4 } console.log(target result); // true目标对象被原地修改从 assignIn 的实现 可以看到该函数会遍历所有源对象逐个合并并在最后return object因此返回值就是被修改后的目标对象本身。2.2 合并多个源对象const target2 { a: 1 }; const source1 { b: 2 }; const source2 { c: 3 }; assignIn(target2, source1, source2); // 结果: { a: 1, b: 2, c: 3 }多个源对象按参数顺序依次合并后面的源对象会覆盖前面源对象或目标对象中的同名属性。2.3 复制继承属性与 assign 的核心区别function Parent() {} Parent.prototype.inherited inheritedValue; const child Object.create(Parent.prototype); child.own ownValue; const target3 {}; assignIn(target3, child); // 结果: { own: ownValue, inherited: inheritedValue }child对象自身的own属性会被复制同时其原型链上Parent.prototype的inherited属性也会被复制。这正是assignIn与assign最本质的区别——后者只会遍历自有属性使用for...of Object.keys()语义而assignIn会沿原型链向上遍历。2.4 数组作为源对象const arr [1, 2, 3]; arr.customProp custom; const target4 {}; assignIn(target4, arr); // 结果: { 0: 1, 1: 2, 2: 3, customProp: custom }数组的索引属性0、1、2以及附加的自定义属性customProp都会被复制。这一行为背后依赖 keysIn 对类数组array-like对象的特殊处理它会把数组视为稠密数组dense array来生成索引键。三、参数与返回值文档对参数的正式定义如下targetany属性被复制到的目标对象。...sourcesany[]提供要复制属性的源对象自有属性和继承属性都会被复制。返回值any——返回修改后的目标对象。目标对象本身被修改并返回。对应到源码中的 TypeScript 重载签名assignIn.tses-toolkit 针对 0 到 4 个源对象提供了精确的交叉类型推断export function assignInT, U(object: T, source: U): T U; export function assignInT, U, V(object: T, source1: U, source2: V): T U V; export function assignInT, U, V, W(object: T, source1: U, source2: V, source3: W): T U V W; export function assignInT, U, V, W, X(object: T, source1: U, source2: V, source3: W, source4: X): T U V W X; export function assignInT(object: T): T; // 无源对象时原样返回目标对象 export function assignInR(object: any, ...otherArgs: any[]): R; // 兜底重载不传任何源对象时目标对象原样返回assignIn(target)的结果就是target本身。传入多个源对象时返回类型是T U V ...的交叉类型TypeScript 能精确推断合并结果的结构。四、别名extend在 Lodash 中extend是assignIn的经典别名es-toolkit 同样保留了这个别名。从源码看extend.ts 只是一个再导出export { assignIn as extend } from ./assignIn.ts;因此以下两种写法完全等价import { assignIn } from es-toolkit/compat; import { extend } from es-toolkit/compat; assignIn({}, source); extend({}, source); // 与上一行行为完全一致4.1 相关变体在compat模块中还有一组与assignIn配套的函数迁移时值得一并了解assignInWith带自定义合并回调的版本见 assignInWith.ts。extendWithassignInWith的别名见 extendWith.ts。assign/defaults/merge仅处理自有属性或不覆盖已存在值的其他合并语义。五、底层实现原理5.1 主循环与单源合并assignIn 的主函数 将每个源对象委托给assignInImpl处理export function assignIn(object: any, ...sources: any[]): any { for (let i 0; i sources.length; i) { assignInImpl(object, sources[i]); } return object; } function assignInImpl(object: any, source: any): any { const keys keysIn(source); for (let i 0; i keys.length; i) { const key keys[i]; if (!(key in object) || !eq(object[key], source[key])) { object[key] source[key]; } } }注意这里有两个关键实现细节键来源是keysIn而非Object.keys。keysIn使用for...in遍历天然包含原型链上的可枚举属性这是复制继承属性这一行为的技术根基。值相同不覆盖优化。条件!(key in object) || !eq(object[key], source[key])表示只有当目标对象中不存在该键、或目标值与源值不相等eq比较即Object.is语义时才执行赋值。这可以避免在属性值相等时触发不必要的 setter 或重复写入是文档中提到的值相同时不覆盖的优化。5.2 keysIn继承属性遍历的基石keysIn 的实现决定了assignIn对各种输入类型的处理行为null/undefined源对象返回空数组不复制任何属性。普通对象 / 函数走for...in收集字符串键含原型链可枚举属性。原型对象prototype本身会过滤掉constructor键prototypeKeysIn。类数组array-like对象按稠密数组语义生成索引键即稀疏数组[1, , 3]会生成0、1、2三个键缺失索引取undefined同时针对 Buffer 与 TypedArray 保留了offset/parent/buffer/byteLength/byteOffset等非索引可枚举属性。其他原始类型先通过Object(object)装箱再取键。这也解释了文档示例中数组、字符串等非常规对象为何也能被正确合并——例如 测试用例 验证了assignIn({}, a)的结果是{ 0: a }字符串被当作类数组处理。5.3 与 assign 的对比对比 assign 的实现它的内部逻辑与assignInImpl几乎一致唯一区别是键来源assign使用keysToolkit仅自有可枚举键而assignIn使用keysIn含继承键。因此特性Object.assignassignassignIn/extend自有可枚举属性✔✔✔原型链继承属性✘✘✔值相同跳过写入✘总是赋值✔eq比较✔eq比较原地修改目标✔✔✔性能原生最快较快较慢额外遍历 值比较六、测试用例中的边界行为assignIn.spec.ts 用 9 组用例锁定了该函数的行为契约可作为实际使用时的行为参考基础赋值assignIn({ a: 1 }, { b: 2 })→{ a: 1, b: 2 }。多源合并后面的源对象覆盖前面已存在的键。覆盖目标属性源对象同名键覆盖目标值。nullish 值也会赋值{ a: null, b: undefined, c: null }会被正常复制不会被跳过。值相同跳过写入当目标对象属性带有 getter/setter 且值与源值相等时不会触发 setter——测试用抛错的 setter 验证了这一点{ a: 1, b: undefined, c: NaN, constructor: Object }均未触发异常因为NaN NaN在eq/Object.is语义下成立。稀疏数组按稠密处理[1, , 3]复制为{ 0: 1, 1: undefined, 2: 3 }。原型对象赋值Foo.prototype.a 1可被直接复制且会过滤constructor。字符串源对象装箱assignIn({}, a)→{ 0: a }。undefined 值属性正确保留{ workId: undefined }合并后键仍然存在。这些用例对迁移 Lodash 代码尤其有价值如果你的旧代码依赖assignIn会连原型属性一起拷或undefined 值键不被丢弃等 Lodash 语义es-toolkit 的实现与测试保证了这些行为可被无缝继承。七、使用建议与总结默认使用Object.assign官方文档明确警告assignIn因继承属性遍历和值比较逻辑而变慢。绝大多数场景下原生Object.assign或对象展开运算符{ ...source }更合适。仅在兼容场景使用assignIn当你的代码库从 Lodash 迁移、且确实依赖复制原型链上的可枚举属性这一语义例如合并带原型方法的配置对象、类实例属性时才需要从es-toolkit/compat引入assignIn。牢记原地修改assignIn会直接修改并返回第一个参数target如需保持原对象不变请先传入空对象assignIn({}, ...sources)。善用类型推断由于源码提供了交叉类型的重载签名合并结果在 TypeScript 中能获得完整的类型提示无需手动断言。总而言之assignIn别名extend是 es-toolkit 兼容层中一个功能完整、语义精确但刻意不建议优先使用的 Lodash 兼容函数。理解它的行为、源码与测试边界能帮助你在 Lodash 迁移项目中做出正确的函数选型并在真正需要继承属性合并时写出正确、可维护的代码。延伸阅读assignIn 官方文档英文assignIn 源码keysIn 源码继承属性遍历实现assignIn 单元测试extend 别名源码compat 模块统一导出入口【免费下载链接】es-toolkitA modern JavaScript utility library thats 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考