Issues with variables

Hello everyone,

I'm having some problem with basic variables manipulations. Here's a code example that does the issue.

let CopyOfOriginal = context.get('CopyOfOriginal') || [];

let OriginalObject = 
{
    payload:'Im original'
}

CopyOfOriginal.push(OriginalObject);

context.set('CopyOfOriginal', CopyOfOriginal);

// Modify after the context.set
OriginalObject.payload = 'Im modified';

// Push without context.set
CopyOfOriginal.push(OriginalObject);

return msg;

I'm expecting the CopyOfOriginal object to contain 'Im original' since it was copied before the modification was made to the OriginalObject.

And why is the last line able to modify the context object without a context.set ?

Thanks for taking time to have a look at this.

I think that you have fallen foul of a JavaScript oddity that only applies to context variables in memory. If you tried the same thing with some other storage mechanisms, you would get the result you expect.

The reason is that assigning in-memory objects happens by reference - in other words, it is the SAME data, not a copy.

If you want to be certain that you have a copy, there is a RED utility method for that - it does a shallow copy. Sorry, I can't remember the method name off the top of my head.

I am not sure about that, at least not with the standard file storage context. At run time it is still in memory, it is just saved to disc occasionally and restored on restart.

let CopyOfOriginal = RED.util.cloneMessage(context.get('CopyOfOriginal')) || [];

1 Like

Thanks for your answer. I digged a little more and found that I could make a shallow copy using

CopyOfOriginal.push({ ...OriginalObject });

Or a safer deep copy using

CopyOfOriginal.push(JSON.parse(JSON.stringify(OriginalObject)));

Use the already built in helpers :grin:

CopyOfOriginal = RED.util.cloneMessage(CopyOfOriginal)

CopyOfOriginal is no longer a reference type

EDIT
Uhh I should have read @Colin's message :angel:

1 Like

Strictly it is still a reference type, but it is referring to a copy, not the original.

1 Like

What would be the best practice way of making a deep copy ?

CopyOfOriginal = JSON.parse(JSON.stringify(OriginalObject));

or

CopyOfOriginal = RED.util.cloneMessage(CopyOfOriginal)

cloneMessage IMO.

cloneMessage, uses the clone package, that walks the object, and guards against circular references.

JSON.parse(JSON.stringify(OriginalObject)) whilst works, can get into trouble with circular references, may also be not as performant, having to construct a string before it can create another object.

although in everyday use - you might not measure such difference, but there will be.

1 Like

And can also lose properties that don't survive the round-trip to JSON and back again. For example, any Date objects.

2 Likes

Greatly appreciated ! Thanks !

I don't think cloneMessage is a deep copy. JSON stringify/parse, as Nick says isn't always type safe and indeed may well fail - always wrap in a try block.

True deep copies in JavaScript are not simple. But there is plenty written online about the subject.

That's why I said "some". :slight_smile: It very much depends on the mechanism used.