Javascript: Object and its references

Javascript objects are passed by reference, and if not handled properly, it might result in a mess with our variables, functions, and arrays. As a basic example of what I am talking about check the following code.

var obj1 = {
    name: "obj1 ",
    type:"book",
    owner:"enterprise"
};

Let's say we want to add a new book, it will be the same except for the name. The first idea that comes to mind, might be to copy the object and just change the name to avoid writing down all the properties, so you might attempt something like this:

var obj2 = obj1;
obj2.name = "obj2";

When we check our obj2, we find the expected result:

console.dir(obj2); // => { name: 'obj2', type: 'book', owner: 'enterprise' }

But when check obj1... BOOM!

console.dir(obj1); // => { name: 'obj2', type: 'book', owner: 'enterprise' }

What just happened is that objects are passed by reference so, obj2 is not an object itself, its a reference to obj1 and when we modify one of them the other one is modified too.

Pointers (Sort of)

Javascript has no pointers but objects references can be stored in variables, which have a similar outcome for users.

To be understood by everyone, let's say that an object is a board and when you reference it, you are sharing it, so any changes made on the board will affect both players.

Primitive types (Once again... sort of)

These are assigned by value so they are completely independent. Javascript specifies 6 primitive types in ES6:

  • Boolean
  • String
  • Number
  • Null
  • Undefined
  • Symbol (ECMAScript 6)
var a = 1;
var b = 2;
b = a; // => b = 1, a = 1
b = 3; // => b = 3, a = 1

Note that arrays and functions are objects too in Javascript. Although arrays only work as copies on browsers while for example other environments such as NodeJS, treats arrays also as references.

Functions

var obj1 = {
    name: "obj1 ",
    type: "book",
    owner: "enterprise"
};

function newBook(book, name) {
    book.name = name;
    return book;
}

var ob2 = newBook(obj1, "obj2");

This function gets book as a parameter but it's an object so the result will be the same as we have seen till now, obj1 is modified too.

There are many ways to solve this issue like cloning the object or creating a new one and copy every property one by one, but there is a small trick that I use frequently:

var obj1 = {
    name: "obj1 ",
    type: "book",
    owner: "enterprise"
};

function newBook(book, name) {
    var newBook = JSON.parse(JSON.stringify(book));
    newBook .name = name;
    return newBook ;
}

var ob2 = newBook(obj1, "obj2");

It seems that I'm copying the book object into another one, which indeed will reference the first object data, but this time I'm marshaling it (JSON.parse(JSON.stringify(book));) Remember that primitive types are passed as values, well, if you marshal the object to String (a primitive type) then you have a brand new object.

A different approach to the same solution, could be achieved using ES6 with:

var newBook = Object.assign({}, book);

you can check more about it at Mozilla documentation page

Assigning a new object

When you assign or reassign an object, it loses all previous references.

var obj1 = {
    name: "obj1 ",
    type: "book",
    owner: "enterprise"
};

function update(book) {
    book = {
        name: "obj1_updated",
        type: "book",
        owner: "enterprise"
    };
}

update(obj1); // => { name: "obj1 ", type: "book", owner: "enterprise" }

What happened in the example above is that we are getting obj1 as a parameter in the update function, so book is now referencing obj1. Then, we assign a new object to book so it loses it's reference to obj1. If we did console.log(book.name); within our update function, the value would be "ob1_updated". What if we did console.log(obj1.name); outside of the function brackets? BOOM! obj1.name remains the same. This happens because when we assigned book a new object, even it was pointing to obj1, it lost the reference to obj1.

Scope

Each variable is accessible from anywhere in a script tag (browser) but this isn't the case for variables declared within functions since they have their own scope.

var global = 15;

function doSomething(){    
    console.log(global); // Works because the variable is declared outside the function
    var scoped = 10;
}

doSomething();
console.log(scoped); // Doesn't work because the variable belongs to the function, it's out of our script scope

Until ES6, JavaScript had no classes, but there is a workaround to use it like a class-based language even if you target ES5. We know that functions are objects, and we are about to know what this is in js.

this (Magic!)

this generally refers to the scope where it's called, and it's undefined in environments like NodeJS and returns window if called from a global scope within browsers. For a more detailed view of this check Mozilla documentation on this

var obj = {
    data: "data",
    print: function() {
      return this.data;
    }
};  
console.log(obj.print()); // => "data"

Merging all these concepts:

var scope = {};

(function(){

    var private = "I belong only to the object";
    this.public = "I am accessible from outside";

}).apply(scope);

console.log(scope); // => { public: 'I am accessible from outside' }

As you can see, we can access those properties or functions declared using this, while the others are kept private to the object itself.

An aside note:

Remeber that objects are references in javascript, which means that:

var obj1 = {
    name:"Jonh"
}

var obj2 = {
    name:"Jonh"
}

console.log(obj1 === obj2); // => false

Comparing objects in JavaScript is tricky because it checks if both use the same reference, otherwise it will return false.
If you need to compare two different objects you can cast them to string before checking.