提问人:sbridewell 提问时间:11/1/2023 最后编辑:sbridewell 更新时间:11/5/2023 访问量:58
我可以通过从 JSON 反序列化来创建键值对映射吗?
Can I create a map of key-value pairs by deserializing from JSON?
问:
我正在尝试通过反序列化 JSON 文档来创建一个实例,但我实际得到的似乎是一种不同类型的对象,没有任何方法。Map<TKey, TValue>
Map<TKey, TValue>
我对 TypeScript 很陌生(我主要使用 C#),所以我使用 Jest 创建了一个小型单元测试来检查是否能完成我想要的操作,特别是我是否可以使用该方法来遍历每个键值对。Map<TKey, TValue>
forEach
function createMap(): Map<number, string> {
const map = new Map<number, string>();
map.set(1, 'foo');
map.set(2, 'bar');
console.log(map); // Console output: Map(2) { 1 => 'foo', 2 => 'bar' }
return map;
}
function iterateUsingForEach(map: Map<number, string>): number {
let counter = 0;
map.forEach((value: string, key: number, map: Map<number, string>) => {
// Normally I'd do something more exciting here than just count the
// elements, but in the interests of simplicity...
counter++;
});
return counter;
}
describe('A Map instantiated using its constructor', () => {
it('can be iterated using map.forEach()', () => {
const map = createMap();
const count = iterateUsingForEach(map);
expect(count).toBe(2); // pass
});
});
到目前为止一切顺利,测试通过了,我有一个键值对列表,可以遍历每个键值对,这就是我想要的。但是填充此对象的数据来自对 Web API 的调用,该调用返回一个 JSON 文档,我需要将其反序列化为 ,因此我添加了更多测试来模拟此用例:Map<number, string>
function deserializeMap(): Map<number, string> {
const json = '{ "1": "foo", "2": "bar" }';
const map: Map<number, string> = JSON.parse(json) /*as Map<number, string>*/;
console.log(map); // Console output: { '1': 'foo', '2': 'bar' }
return map;
}
function iterateUsingObjectDotEntries(map: Map<number, string>): number {
let count = 0;
for (let [key, value] of Object.entries(map)) {
count++;
}
return count;
}
describe('A Map instantiated using deserialization', () => {
it('can be iterated using map.forEach()', () => {
const map = deserializeMap();
const count = iterateUsingForEach(map); // fail - TypeError: map.forEach is not a function
expect(count).toBe(2);
});
it('can be iterated using Object.entries(map)', () => {
const map = deserializeMap();
const count = iterateUsingObjectDotEntries(map);
expect(count).toBe(2); // pass
});
});
第一个测试失败了,因为 ,并且查看控制台输出,似乎这次实际上根本不是一个实例,而是一个具有名为 和 的属性的普通对象,它可能没有原型,因此没有方法,尽管该函数显式声明其返回类型为,甚至尝试将其返回值转换为该类型(我不确定该强制转换是否有任何影响 - 编辑根据 jcalz 的评论,我已经评论了“cast”,因为它实际上不是一个 cast,而是一个类型断言)。map.forEach is not a function
map
Map<number, string>
1
2
deserializeMap
Map<number, string>
我猜 TypeScript 编译器执行的类型检查并没有抱怨这一点,因为即使它实际上不是该类型的实例,它仍然与类型兼容?map
Map<number, string>
第二个测试通过,因为它没有将对象视为键值对列表,而是循环访问每个属性名称和值。从功能上讲,使用可以做我想要的,但我觉得它类似于使用 .net 在运行时发现对象属性的名称和值,所以我想知道这是否会对性能产生影响。因此,我添加了这个基准来比较这两种方法的性能:map
Object.entries(map)
Reflection
// Requires the tinybench package, to install it run:
// npm install tinybench --save-dev
// Also requires the following import:
// import { Bench } from 'tinybench';
it('runs a benchmark to compare the performance of the two approaches', async () => {
const bench = new Bench({ iterations: 10000 });
const instantiated = createMap();
const deserialized = deserializeMap();
bench
.add('map.forEach()', () => {
iterateUsingForEach(instantiated);
})
.add('Object.entries(map)', () => {
iterateUsingObjectDotEntries(deserialized);
});
await bench.run();
console.table(bench.table());
});
我已经运行了好几次,结果各不相同,但似乎需要 1.5 到 2 倍的时间来完成相同的工作。输出示例:Object.entries(map)
map.forEach()
┌─────────┬───────────────────────┬─────────────┬───────────────────┬──────────┬─────────┐
│ (index) │ Task Name │ ops/sec │ Average Time (ns) │ Margin │ Samples │
├─────────┼───────────────────────┼─────────────┼───────────────────┼──────────┼─────────┤
│ 0 │ 'map.forEach()' │ '1,739,642' │ 574.8305975247757 │ '±8.27%' │ 869822 │
│ 1 │ 'Object.entries(map)' │ '1,074,330' │ 930.8122055151725 │ '±1.02%' │ 537167 │
└─────────┴───────────────────────┴─────────────┴───────────────────┴──────────┴─────────┘
所以我的问题是,我是否可以将 JSON 文档反序列化为 的实际实例,包括其原型和方法,而不是仅与该类型类型兼容的对象,以便利用更快的性能?Map<number, string>
map.forEach()
编辑:我不认为这是从后端接收到打字稿中前端接口的投射数据的重复,因为这似乎是关于将具有一个形状的对象的属性映射到具有不同形状的不同对象的属性,而我正在尝试从具有原型和方法的 JSON 创建一个对象。
答: 暂无答案
评论
as
JSON.parse()
除非您传入类似函数之类的东西作为第二个参数,否则不会产生 s。因此,影响性能的唯一方法是更好地实现该功能(也许可能会更快)。这与 TypeScript 本身关系不大;它只是 JavaScript。如果你在这里寻找最佳性能,你可能想添加关于 JavaScript 和性能的标签,否则答案只是“没有内置的方法来做到这一点,你必须实现它”。我们应该如何进行?Map
iterate...
for (let k in obj) {...}
Map
JSON.stringify()
JSON.parse()