nodejs以对象做对象的key导致value一直被覆盖

时间:2022-07-01 13:04:48
问题描述

在开发中,实现技能状态的事件监听功能时,将状态对象作为key,存入事件管理器的监听列表,如下图:

nodejs以对象做对象的key导致value一直被覆盖

实现后,运行程序,并没有报错,但是当某个事件发生时,只有一个状态被触发监听,而大多数状态在创建时,就监听了该事件,正确的表现应当是所有监听该事件的状态对象都被触发。

原因分析

首先判断是状态实现问题,但是每个状态都是继承AbstractState这个父类,并没有重新实现父类的事件监听函数,检查父类监听代码实现,未发现问题。

代码检查没有发现问题,看着是正常的,但实际效果确实有问题,于是添加打印,在addEventListener之前打印监听列表,addEventListener之后也打印监听列表,

打印的结果显示,addEventListener之前如果监听列表为空,那么addEventListener之后会成功添加一个监听,但是如果addEventListener之前监听列表有一个监听了,那么addEventListener之后,监听列表只会剩下最新添加的那个监听!这就是问题所在了,但是为什么会出现这种情况呢?为什么新的对象会覆盖旧的?

将本次遇到的问题简单化成如下例子:

nodejs以对象做对象的key导致value一直被覆盖

可以看到,b实际上一直只有一个key,即'[object Object]'这个字符串,变化的只是value,在JS中,创建对象时,不论以什么类型的值做Key,JS会默认调用toString()方法将该key转成字符串类型,如数字类型:

nodejs以对象做对象的key导致value一直被覆盖

可以看到key 1 变成了 '1',而当key时对象时,object.toString()的结果只会等于'[object Object]',如下图:

nodejs以对象做对象的key导致value一直被覆盖

这就是上面为什么b对象永远只会有一个key =  '[object Object]'的原因,JS不支持对象使用toString()方法,即不支持对象以对象做key。

另外,对象转字符串,JS提供JSON.Stringify()特殊方法。

解决方案

鉴于上面的分析结果,如果还是执意想要用状态对象做key的话,可以使用JSON.Stringify(state),但这显得不够优雅,最后的解决方案是在每个状态对象中,增加一个uuid属性,以该uuid作为Key,存入监听列表中。