by James Coglan

Menu

One thing that’s tripped me up recently, and is the cause of some bugs in
Sylvester that I’m ironing out, is JavaScript’s handling of array and
object variables. When you create an array – [3,4,5] – or an object –
{foo: 12, bar: 'something'}, the variable name you give it is not a storage
vessel for the value you’re assigning, it’s a direct reference to the value in
memory. For example, with strings you can do this:

So by saying bar = foo, you’re telling the compiler, “take the value stored in
the variable foo and copy it into the variable bar”. The value ‘a string’ is
stored in memory twice – once for each variable, until bar is reassigned.

Now when you say bar = foo, you are not copying foo’s value into the space
in memory represented by bar. There remains only one copy of the array in
memory, and both fooandbar are references to it. If you want to copy an
array, you need to use the following:

The reference vs. value, um, thing (I won’t call it a problem as there are some
very useful things you can do with it) also means that any function that takes
an array or an object as an argument receives the original variable you pass to
it, not a copy of its value. Take a look at the following:

You might be fooled into thinking that assigning the incoming argument to a
local variable and pushing values onto that means that the argument you pass
will not be modified, but this is not the case. To leave the argument untouched
by the function call, you need to explicitly copy its value in memory using
localArg = arg.slice() or by looping through its elements.

The place where this specifically comes up in Sylvester is allowing methods to
accept both Vector objects (with an elements property) and arrays as
arguments. Some methods need to modify the element array after grabbing it from
the argument, and the effect of this depends on what you’re trying to do.

A fairly useless method, but it illustrates the point. The first line just grabs
the elements propery if it exists, otherwise it grabs the raw argument. In the
second line, V is thus a reference to the array fed into the function. We call
push(10) on this reference, which means we push 10 onto the one copy of the
array living in memory, so foo mirrors this change. In the third line, V is
reassigned. It is no longer a reference to the argument, but a reference to the
newly created object – Vector.create([3,4,5]). This does not assign this
new object to the function’s argument.

To sum up, then: array/object variables do not store values, they refer to them.
Calling methods on these references modifies the return value of all connected
references, but reassigning to references leaves all connected variables
unchanged.

—

If you’ve enjoyed this article, you might enjoy my recently published book
JavaScript Testing Recipes. It’s
full of simple techniques for writing modular, maintainable JavaScript apps
in the browser and on the server.