Object Craziness

Oh Javascript, you sneaky bastard.

Let's say you have an array of objects:

var arr = [{}, {}, {}]

Can you use Array.prototype.slice() on them?

MDN defines .slice() as

"...(a) method returns a shallow copy of a portion of an array into a new array object."

So yes, in theory, you can...but be careful.

Javascript has some really subtle "gotchas" when it comes to objects, which can make debugging difficult.

Let's back-track and talk about pass-by-reference and pass-by-value in Javascript, because there's an important difference between the two, which can send you down a sad hole of slight annoyance.

In JS, primitive data types (string, number, etc.) are passed-by-value. This means that variables point to specific values, not references.

var x = 1;
var y = x;
console.log(y) //--> 1
x = 2;
console.log(y) //--> 1
console.log(x) //--> 2

When we declare that y = x, we are setting y equal to 1. y does not point to x, it points to x's value of 1. So when we change the value of x to 2, y continues being 1, because we have not changed y.

Objects, however, dance to their own tune. They can be both pass-by-value AND pass-by-reference. It depends on the situation.

Let's play around with two objects: obj1 and obj2. We'll have a reference variable to keep track of things.

var status = {hello: 'unchanged'}

var obj1 = {}, obj2 = {};

We will have them both refer to status.

obj1.x = status;
obj2.x = status;
console.log(obj1.x) //--> Object {hello: 'unchanged'}
console.log(obj2.x) //--> Object {hello: 'unchanged'}

That should be obvious right?
We know that whatever we changed obj1 to, it won't affect obj2.

obj1 = {x: {status: 'changed'}};
console.log(obj1.x) //--> Object {hello: 'changed'}
console.log(obj2.x) //--> Object {hello: 'unchanged'}

If you're lost at this point, this post is not for you.

But what if you changed obj1.x.hello?

status = {hello: 'unchanged'};
obj1 = {}; obj2 = {};
obj1.x = status;
obj2.x = status;
obj1.x.hello = 'changed';

What are obj1.x and obj2.x now? :)

And here we see these same principles being applied in closures.

function createObj(ref) {
    return function() {
        return {
            x: ref
        }
    };
}

var functionFactory = createObj({hello: 'unchanged'});
var obj3 = functionFactory(), obj4 = functionFactory();

console.log(obj3.x) //--> Object {hello: 'unchanged'}
console.log(obj4.x) //--> Object {hello: 'unchanged'}

But what about...

obj1 = {
    x: obj4.x
};

obj1.x.status = 'changed';

console.log(obj3.x)
console.log(obj4.x)

What would we console log?

Wee!

The deepcopy library from npm has been quite useful in helping get around this mess.