825Prototypal Inheritance and it’s Problem with Objects

Prototypal inheritance in Javascript is one of these features that sets it apart from other languages. A quick reminder:

var myDog = {};
myDog.legs = 4;
myDog.type = "Dachshund";
myDog.color = "brown";
myDog.says = "wau wau wau";

var yourDog = Object.create(myDog);
yourDog.says = "wan wan wan";

console.log(yourDog.type, yourDog.says);
> "Dachshund", "wau wau wau";

console.log(myDog.type, myDog.says);
> "Dachshund", "wan wan wan";

myDog is the prototype for yourDog, together they for a prototype chain. Any properties that would be added in myDog would also be accessible in yourDog. If yourDog adds a property with the same name (yourDog.says), this new property is returned when called. If a property is not defined, but present in the prototype, it will shine through.

So far so good.

Let's say we have an Array of dogs and we would like to prototypal inherit that whole array.

var myDogs = []
// fill array with dog objects

Following the pattern from above, the way to make a yourDogs array should be like that:

var yourDogs = Object.create(myDogs);

Let's update the first dog of the new yourDogs array to say something else:

yourDogs[0].says = "wow wow wow";

One would expect, that - because of the prototypal inheritance - that the changed value would only hide the original value. Let's check:

console.log(myDogs[0].says);
> "wow wow wow";

Ouch. The value was not as expected added to the prototypal branch only, but also to the original object. The prototype object and its chained offspring have the same object. Why is that? The answer is, that only variable types of Number, String, Boolean, etc are passed by value. Objects - and Arrays are objects - are passed by reference. (Check with typeof() to confirm the type of a variable.). That means the objects in our arrays are one and the same, and therefore any change in would will also occur in the other.

Seems logical, but still expectations are betrayed.

Let's consider the following case, this time without dogs. You have an array with objects and you want to apply a filter to some properties of the objects, which should be collected in another array. But - and this is crucial - you don't want the original array objects to change.

var original = [];
// add many, many objects

Because we saw before, that any changes in a new object would also reflect in the old one, we might be tempted to make a deep copy of the new one and then make our changes.

var copy = [];
$.extend(true, copy, original);     // Yes, that's jQuery
copy.map(function(element) {
    // make our changes to element
    //return element;
}

It's works, but it's rather kludgy. It takes quite a long time to make the deep copy and the prototypal chain breaks by doing that. Note also that the return of the map() function is not assigned to a new variable, and that the function does not need to return anything actually - because we are operating on the object directly.

A better solution would be this:

var better = copy.map(function(element) {
    var protoype = Object.create(element);  // create prototypal inheritance
    // make our changes to element
    return protoype;
}

better is now an array of objects which are prototypal linked to the original array. Any newly added properties will only be added to the new array - covering any properties which are already present in the original one.

console.log(original[0].dog.says);
> "wau wau wau"
console.log(better[0].dog.says);
> "wau wau wau"

better[0].dog.says = "wuff wuff wuff";

console.log(original[0].dog.says);
> "wau wau wau"
console.log(better[0].dog.says);
> "wuff wuff wuff"

That would I would have expected in the beginning. The nice things about Javascript is, that even when in works in unexpected ways, it's usually fairly trivial to align it again with your imagination. And sorry for bringing the dogs back.