This is not the kind of thing for which performance should be the ultimate guiding factor, especially without a profiler in hand. Let maintainability and productivity be the guiding force here until you start measuring hotspots in hindsight.
That said, just as a general rule of thumb, the more local your state changes and accesses are, typically the faster they will be. More local accesses to local variables are preferable from a performance standpoint than more access to fewer but more global variables.
Local State is Fast
Optimizing compilers do their best when the most amount of information is readily available. Effective register allocation and instruction selection is easier when analyzing the local variables of a function than globals variables with a much broader scope than the function which could be modified and accessed anywhere.
The key to efficiency often revolves around loading from slower but bigger memory (DRAM,e.g.) into faster but smaller memory (L1 cache line and then register). Making code fast means keeping and using frequently-accessed memory as much as possible inside those faster but smaller forms of memory (register, e.g.). This is easier for the compiler to do when your variables have a very local scope, since it doesn't have to look outside that scope to see whether it needs to spill the variable to the stack or can keep it inside the register, for example.
So as a general rule of thumb, prefer more local state with shorter scopes to more global states with wider scopes. That not only tends to improve efficiency but also reduce human errors*.
* Note that this is opposite from an assembly mindset. In assembly, it's helpful to reuse registers as much as possible to avoid stack spills. In languages that have variables instead of direct register access, more local variables helps the compiler figure out what registers it can reuse.
Specific Example
In this specific case, adding members and removing them from an associative structure (objects fit this case in JS) tends to be more expensive than creating one and simply discarding it as a whole. These lines:
delete attacker.dmg;
delete defender.dmg;
... specifically tend to be more costly than constructing some temporary object and simply destroying it as a whole when you're finished with it than adding members to a more global object and then removing them when done.
As a plus, this should also be a little less error-prone so you get that bonus as well. As a second plus, assuming attacker
and defender
have a fairly broad scope, this function that creates this dmg
variable would avoid side effects to those objects, and therefore would be thread-safe since it's only dealing with local states (assuming this is all it's doing and not modifying other globals).
If you're really fussy about it, it would be best to create attacker damage and defender damage as two separate scalars instead of aggregated into an object and pass both forms of damage to this battle
function. But that's really degrading the code readability, and something you should only do with a profiler in your hand that tells you this is a top hotspot.