
es-toolkit 的 create 函数基于原型创建对象的 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本篇技术指南聚焦 es-toolkit 的 Lodash 兼容层es-toolkit/compat中的create函数。该函数用于创建一个继承自指定原型的新对象并可选地为新对象附加自有属性是对象式继承与原型链编程中的常用工具。读完本文你将掌握create的完整用法、参数与返回值语义、它在属性复制上的边界行为仅复制可枚举字符串键、忽略 Symbol 键与不可枚举属性并通过源码与测试用例深入理解其底层实现原理。概述create是什么create是 es-toolkit 兼容层es-toolkit/compat提供的对象工具函数语义与 Lodash 的_.create一致创建一个新对象使其原型指向传入的prototype对象并可选地在新对象上添加额外属性。const obj create(prototype, properties);它在项目中的应用场景包括实现原型链式继承如 create.spec.ts 中构造Circle.prototype create(Shape.prototype)的经典继承模式在不想直接修改某个对象原型的前提下快速派生一个带有默认值的新对象创建不携带原型污染的新对象传入null原型。性能提示官方文档明确指出由于存在复杂的属性处理逻辑create函数相对较慢建议优先使用现代 JavaScript 原生 APIObject.create。create主要用于 Lodash 兼容场景。基本用法在项目中通过以下方式导入import { create } from es-toolkit/compat;基于原型创建对象并附加属性// 基本用法 const person { greet() { console.log(Hello, my name is ${this.name}); }, }; const john create(person, { name: John }); john.greet(); // Hello, my name is John // 检查方法继承关系 console.log(greet in john); // true console.log(john.hasOwnProperty(greet)); // false继承属性 console.log(john.hasOwnProperty(name)); // true自有属性从上述示例可以看到create生成的john对象通过原型链访问到person上的greet方法而name则是john的自有属性。复杂原型继承字段与方法覆写const animal { type: animal, makeSound() { console.log(Some generic sound); }, }; const dog create(animal, { breed: Golden Retriever, name: Buddy, makeSound() { console.log(Woof!); }, }); console.log(dog.type); // animal继承自原型 console.log(dog.breed); // Golden Retriever自有属性 dog.makeSound(); // Woof!覆写的方法dog同时具备继承自animal的type字段和makeSound方法又通过properties参数定义了自己的breed、name字段并覆写了makeSound。这正是“原型继承 自有属性增强”的典型组合。null 原型与空对象原型// null 原型 const cleanObj create(null, { data: value }); console.log(cleanObj.toString); // ƒ toString() { [native code] }null 等价于 {} // 继承空对象 const empty create({}); console.log(Object.getPrototypeOf(empty)); // {}空对象注意文档中的示例说明create(null, ...)中传入的null原型实际被等价处理为{}因此新对象仍然拥有继承自Object.prototype的方法如toString。属性复制规则只复制可枚举的字符串键create在复制properties时遵循一条严格规则只复制properties对象上可枚举的、字符串类型的自有属性。继承属性、Symbol键以及不可枚举属性都不会被复制。import { create } from es-toolkit/compat; const proto { inherited: true }; const props { visible: yes, [Symbol(hidden)]: no, // Symbol 键不会被复制 }; // 添加不可枚举属性 Object.defineProperty(props, hidden, { value: secret, enumerable: false, }); const obj create(proto, props); console.log(obj.visible); // yes console.log(obj.hidden); // undefined不可枚举 console.log(obj[Symbol(hidden)]); // undefinedSymbol 键 console.log(obj.inherited); // true继承自原型参数与返回值项类型说明prototypeT extends object新对象要继承的原型对象propertiesU extends object可选要添加到新对象上的属性返回值T U一个新的、继承自prototype且携带指定属性的对象类型签名见 create.tsexport function createT extends object, U extends object(prototype: T, properties?: U): T U源码级原理剖析create的完整实现位于 src/compat/object/create.ts核心逻辑仅需三步我们逐一拆解。第一步构造原型对象const proto isObject(prototype) ? Object.create(prototype) : ({} as unknown as T);当prototype是合法对象时判断逻辑来自 isObject.ts值不为null且类型为object或function底层直接调用原生Object.create(prototype)建立原型链当prototype为null、undefined或其他原始值时回退为一个普通空对象{}。这也印证了文档中“null等价于{}”的行为说明。测试用例同样覆盖了这一分支should accept a falsey prototype与should accept a primitive prototype见 create.spec.ts验证了传入假值falsey含null/undefined和原始值如数字、字符串时create仍能返回一个isObject为true的对象不会抛出异常。第二步枚举 properties 的键const propsKeys keys(properties);这里复用的是兼容层的keys函数src/compat/object/keys.ts它返回对象自有可枚举属性名的数组。其实现要点对类数组对象数组、类数组、Buffer、类型化数组会生成对应的数字索引键并针对Buffer补充offset/parent、针对类型化数组补充buffer/byteLength/byteOffset等可枚举的非索引属性对普通对象走Object.keys(Object(object))天然只包含可枚举的字符串键——这从源码层面解释了“Symbol 键和不可枚举属性不会被复制”的规则若对象是原型对象isPrototype还会过滤掉constructor键。因此create的“只复制可枚举字符串键”约束本质上来源于keys的枚举语义而不是在create内部另做判断。第三步逐个赋值for (let i 0; i propsKeys.length; i) { const key propsKeys[i]; const propsValue properties[key as keyof U]; assignValue(proto, key, propsValue); }每个键通过内部工具assignValuesrc/compat/_internal/assignValue.ts写入新对象export const assignValue (object: any, key: PropertyKey, value: any): void { const objValue object[key]; if (!(Object.hasOwn(object, key) eq(objValue, value)) || (value undefined !(key in object))) { object[key] value; } };assignValue的赋值行为等价于 Lodash 的baseAssignValue仅当目标对象不是“已拥有该键且新旧值相等”或者“值为undefined且目标对象原型链上不存在该键”时才执行写入。这意味着如果新对象此时原型链上可能已存在同名属性已有相同值的自有属性赋值会被跳过当新属性的值为undefined且原型链上存在同名键时该赋值会被跳过从而不会用undefined遮蔽原型上的同名属性。一处值得注意的细节Shadowing 行为测试用例should assign properties that shadow those of prototypecreate.spec.ts验证了const Foo function (this: { a: number }) { this.a 1; } as unknown as { new (): any }; const object create(new Foo(), { a: 1 }); expect(keys(object)).toEqual([a]);即当properties中的键与原型上的属性同名时新对象会以自有属性的形式遮蔽shadow原型属性keys(object)结果中只会出现该自有键。继承模式示例作为方法 iteratee 使用create还可以直接作为高阶函数的 iteratee 使用。测试用例should work as an iteratee for methods like _.mapcreate.spec.ts展示了如下用法const array [{ a: 1 }, { a: 1 }, { a: 1 }]; const objects map(array, create{ a: number }, any);由于create接受(prototype, properties)两个参数而Array.prototype.map的回调签名是(value, index, array)二者天然兼容——此时index会被当作可选的properties参数传入。由于数字索引键不可枚举实际效果等价于仅基于元素创建原型链对象。与原生Object.create的对比维度createes-toolkit/compat原生Object.create原型继承支持底层即调用Object.create支持直接指定附加属性支持第二个参数但只复制可枚举字符串键Symbol 键与不可枚举属性会被忽略properties参数通过Object.defineProperties语义处理可定义任意描述符属性写入语义经过assignValue的“相同值跳过 / undefined 不遮蔽”逻辑严格按属性描述符定义性能相对较慢复杂属性处理逻辑更快是推荐做法兼容性为 Lodash 用户提供一致行为现代 JavaScript 标准 API官方文档的结论非常明确在非兼容场景下应优先使用Object.createcreate的价值在于为从 Lodash 迁移到 es-toolkit 的项目提供行为一致的替代实现。使用建议新项目优先用Object.create如果不需要 Lodash 兼容行为直接使用原生 API 更高效迁移项目放心替换从 Lodash_.create迁移到es-toolkit/compat的create导入路径改为import { create } from es-toolkit/compat即可行为保持一致注意属性复制边界若你的properties中包含 Symbol 键或通过Object.defineProperty定义的不可枚举属性create不会复制它们请改用其他方案利用类型推断createT, U的返回值类型为T U调用方可以同时获得原型类型与属性类型的完整类型信息享受类型安全的原型链编程体验。【免费下载链接】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),仅供参考