es-toolkit compat 的 entriesIn / toPairsIn:将对象(含继承属性)转换为键值对数组的完整指南
【免费下载链接】es-toolkitA modern JavaScript utility library that's 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkit
entriesIn是 es-toolkit 兼容层(es-toolkit/compat)中toPairsIn的别名,二者在功能上完全等价:把对象、Map或Set转换为[key, value]键值对数组,并且会包含原型链上的可枚举继承属性。本文以 entriesIn 官方参考文档 为骨架,结合其指向的 toPairsIn 完整文档 以及仓库源码,系统讲解该函数的用法、边界行为、底层实现与性能注意事项,读完即可在需要遍历"自有 + 继承"属性键值对的场景中正确选用它。
entriesIn 是什么:一个指向 toPairsIn 的别名
在 es-toolkit 的 compat 层中,entriesIn并非独立实现,而是toPairsIn的直接导出别名。官方文档对此有明确说明:
entriesInis an alias oftoPairsIn. See thetoPairsIndocumentation for details.
从源码可以印证这一点。entriesIn.ts 全文只有一行:
export { toPairsIn as entriesIn } from './toPairsIn.ts';也就是说,entriesIn与toPairsIn是同一个函数引用,二者用法、返回值、边界行为完全一致。这也是为什么官方文档将entriesIn作为别名收录在 toPairsIn 页面之下,并为其单独保留了重定向文档。
在测试用例中同样验证了这一别名关系。entriesIn.spec.ts 的核心断言就是:
it('should be an alias of toPairsIn', () => { expect(entriesIn).toBe(toPairsIn); });基础用法:把对象转换为键值对数组
使用entriesIn(或等价的toPairsIn)时,从es-toolkit/compat导入即可:
import { toPairsIn } from 'es-toolkit/compat'; // 普通对象转换 const object = { a: 1, b: 2 }; toPairsIn(object); // => [['a', 1], ['b', 2]]它的类型签名(见 toPairsIn.ts)支持两种形态:
export function toPairsIn<T>(object?: Record<string, T> | Record<number, T>): Array<[string, T]>; export function toPairsIn(object?: object): Array<[string, any]>;- 参数
object:要转换的对象(普通对象、Map、Set均可)。参数是可选的,传入null或undefined时返回空数组[]。 - 返回值:
Array<[string, any]>,即键值对数组,包含继承属性。
与 toPairs 的关键区别:包含原型链属性
toPairsIn与toPairs最大的不同在于:toPairsIn会枚举原型链上的可枚举属性,而toPairs只处理对象自身的属性。
官方文档给出的继承属性示例:
import { toPairsIn } from 'es-toolkit/compat'; function Parent() { this.inherited = 'value'; } Parent.prototype.proto = 'property'; const child = new Parent(); child.own = 'own'; toPairsIn(child); // => [['inherited', 'value'], ['own', 'own'], ['proto', 'property']]可以看到,constructor之后的原型属性proto、构造函数内赋值的inherited以及实例自身的own都被纳入了结果。
对应的测试用例见 toPairsIn.spec.ts:Foo.prototype.b = 2会被正确收录进结果数组。
对 Map 与 Set 的特殊处理
除了普通对象,toPairsIn还能直接转换Map和Set,这是它与原生Object.entries()的一个重要差异(Object.entries对Map/Set只会返回空数组)。
官方文档示例:
import { toPairsIn } from 'es-toolkit/compat'; // Map 对象转换 const map = new Map([ ['key1', 'value1'], ['key2', 'value2'], ]); toPairsIn(map); // => [['key1', 'value1'], ['key2', 'value2']] // Set 对象转换 const set = new Set([1, 2, 3]); toPairsIn(set); // => [[1, 1], [2, 2], [3, 3]]注意Set的转换规则:每个元素被映射为[value, value]形式(键和值相同),而不是[index, value]。这一行为在 toPairsIn.ts 中通过类型分支实现:
if (object instanceof Set) { return setToEntries(object); } if (object instanceof Map) { return mapToEntries(object); }对应的 setToEntries.ts 实现为:
export function setToEntries<T>(set: Set<T>): Array<[T, T]> { const arr = new Array<[T, T]>(set.size); const values = set.values(); for (let i = 0; i < arr.length; i++) { const value = values.next().value as T; arr[i] = [value, value]; } return arr; }而 mapToEntries.ts 则通过并行迭代Map的 keys 与 values 迭代器来组装结果:
export function mapToEntries(map: Map<any, any>) { const arr = new Array(map.size); const keys = map.keys(); const values = map.values(); for (let i = 0; i < arr.length; i++) { arr[i] = [keys.next().value, values.next().value]; } return arr; }测试用例 toPairsIn.spec.ts 分别验证了Map([['a', 1], ['b', 2]])与Set([[1, 1], [2, 2]])的转换结果。
底层实现:keysIn 与遍历逻辑
对于普通对象,toPairsIn并不直接使用for...in,而是先调用 compat 层的keysIn获取"自有 + 继承"的键集合,再逐键取值组装:
const keys = keysInToolkit(object); const result: Array<[key: string, value: any]> = new Array(keys.length); for (let i = 0; i < keys.length; i++) { const key = keys[i]; const value = object[key as keyof typeof object]; result[i] = [key, value]; } return result;(见 toPairsIn.ts)
其中keysIn(见 keysIn.ts)承担了复杂的键收集逻辑:
- 传入
null/undefined时返回空数组; - 对类数组对象(
isArrayLike)按数组语义处理,为每个索引生成字符串形式的键,再合并其余继承键; - 对原型对象(
isPrototype)会过滤掉constructor键; - 对普通对象则退化为
for...in收集所有可枚举键(见keysInImpl,keysIn.ts)。
这也解释了为什么toPairsIn能正确处理字符串、带length属性的对象等特殊输入。测试用例验证了这类边界:
// 字符串转换(见 toPairsIn.spec.ts L74-L82) toPairsIn('xo'); // => [['0', 'x'], ['1', 'o']] // 带 length 属性的对象(见 toPairsIn.spec.ts L43-L52) const object = { '0': 'a', '1': 'b', length: 2 }; toPairsIn(object); // => [['0', 'a'], ['1', 'b'], ['length', 2]]性能提示:何时应该改用原生 API
官方文档在 toPairsIn 页面顶部给出了明确的警告:
由于要处理继承属性、
Map、Set等复杂逻辑,toPairsIn的运行速度较慢。如果不需要继承属性,请使用更快、更现代的Object.entries();如果需要继承属性,请直接使用for...in循环。
原因从源码结构即可看出:toPairsIn的完整调用链包含instanceof判断、keysIn内部的isArrayLike/isPrototype/isTypedArray/isBuffer等多项检测,加上对Map/Set迭代器的封装,比原生 API 的常数开销大得多。
实际选型建议:
| 需求 | 推荐方案 |
|---|---|
| 只需对象自身可枚举属性 | Object.entries(object)(原生,最快) |
| 需要含继承属性的键值对 | for...in循环 + 手动收集,或toPairsIn |
需要转换Map/Set | toPairsIn(map)/toPairsIn(set),或手动Array.from(map) |
边界行为与注意事项总结
综合文档、源码与测试,entriesIn/toPairsIn的关键行为可归纳如下:
- 空值安全:传入
null或undefined返回[],不会抛错(toPairsIn.ts,测试见 toPairsIn.spec.ts); - 继承属性包含:原型链上的可枚举属性会出现在结果中,这是与
toPairs、Object.entries()的本质区别; Map:按插入顺序输出[key, value]对;Set:输出[value, value]对(键值相同);- 结果顺序:普通对象的输出顺序取决于
for...in的枚举顺序(字符串键按插入序、整数键按升序),对顺序敏感的场景建议自行排序; - 别名等价:
entriesIn与toPairsIn是同一个函数,可互换使用。
如需进一步阅读完整文档与实现,可参考 toPairsIn 参考文档、toPairsIn 源码、keysIn 源码 及对应的 toPairsIn 测试。
【免费下载链接】es-toolkitA modern JavaScript utility library that's 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkit
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考