Javascript’s prototypal inheritance, for a C# developer

Yan Cui

I help clients go faster for less using serverless technologies.

The other day I put up a post with some quick bullet points about objects in javascript to help someone from a static, strongly typed language (like C# or Java) background understand javascript’s dynamic and loose type system. One important thing I haven’t talked about yet is the way inheritance work in javascript, which again, is very different from those found in C# or Java.

Most object oriented languages like C# and Java use classes to define types, which are basically blueprints for creating objects, this approach to inheritance is referred to as classical inheritance. Javascript on the other hand, has no classes, instead objects inherit from other objects, this approach is referred to as prototypal inheritance.

Constructor functions

You can use functions, which themselves are objects to act as constructors and provide blueprints for creating objects:

function Person(name, age) {
    // properties
    this.Name = name;
    this.Age = age;

    // methods
    this.sayHello = function () {
        return "Hello, my name is " + this.Name + "!";
    }
}

This creates a new object Person.prototype, which all new Person objects will inherit from. You can then use the new operator to create a new Person object using this constructor:

var person = new Person("John", 10);

The person object inherits all the members (properties and functions) that are defined by the constructor function above, as can be seen in Chrome’s debugger (CTRL+SHIFT+J):

image

Dynamically modifying the prototype

It is possible to make changes to Person.prototype object outside of its constructor function, to add a sayBye function to the Person.prototype object:

Person.prototype.sayBye = function (recipient) {
    return "Good bye, " + recipient + "!";
}

Once defined, this new function will be available on all Person objects, even if they were created prior to the new function:

var goodbyeMsg = person.sayBye("Yan"); // returns "Good bye, Yan!"

Understanding how members are looked up

This is all the code we have written so far:

function Person(name, age) {
    // properties
    this.Name = name;
    this.Age = age;

    // methods
    this.sayHello = function () {
        return "Hello, my name is " + this.Name + "!";
    }
}

var person = new Person("John", 10);

Person.prototype.sayBye = function (recipient) {
    return "Good bye, " + recipient + "!";
};

var goodbyeMsg = person.sayBye("Yan"); // returns "Good bye, Yan!"

If you run the code to the end, you will see the sayBye function we added at the end of the example:

image

It falls under the person object’s __proto__ member rather than on the object itself (as is the case for the constructor defined members – Name, Age and the sayHello function) but it’ll still be accessible directly on the person object.

When accessing a member on an object, Javascript will check in that object first, if the member is not found it will then follows the inheritance chain until either the member is found or it will return undefined if it has exhausted all the objects on the inheritance chain.

In order for javascript to keep track of this chain and figure out where next to look for a member, an internal property __proto__ is used to point to the next object in the chain. Because every object is ultimately inherited from Object, it’ll be at the top of every object’s inheritance chain and the last object you’ll be able to fetch if you continuously invoke the __proto__ member before you get undefined returned:

myObj.__proto__.__proto__….

Extending objects

Normally to extend an object you need to perform a two step process:

1. Invoke the base constructor

function Employee(name, age, salary) {
    // equivalent to calling base(name, age) in C#
    // this invokes the base class's constructor on the current object
    Person.call(this, name, age);

    // new property and method
    this.Salary = salary;
    this.reportSalary = function () {
        return "Hi, my name is " + this.Name + ", my salary is " + this.Salary + ".";
    }
}

As you can see, this invokes the Person constructor function (see above) on the current object, if you try to create a new object using this constructor:

var employee = new Employee("Jane", 20, 12000);

this is what the object looks like:

image

Notice anything missing? What happened to the sayBye function we added to the Person object outside of its constructor?

Well, because the sayBye function is not defined in the Person object’s constructor function and all we’ve done so far is to invoke that function to set up some properties and functions for a new Employee object so of course it won’t include anything that’s dynamically added to the object outside of its constructor function.

2. Set up the inheritance relationship

This second step is simple but crucial, this is how you set up the ‘relationship’ between the Person and Employee objects:

// make Employee inherit from Person
Employee.prototype = new Person();

Doing this tells Javascript to use Person as the prototype for Employee, i.e. prototypal inheritance’s way of saying using Person as superclass to Employee. Now that you’ve done this step, if you try to create a new Employee object now this is what your object looks like:

var employee = new Employee("Jane", 20, 12000);

image

Checking an object’s inheritance chain

You can check if an object appear in another object’s inheritance chain using the isPrototypeOf function. For example, using the Person and Employee constructor functions (remember, they are objects) defined above:

var person = new Person("John", 10);
var employee = new Employee("Jane", 20, 10000);

Employee.prototype.isPrototypeOf(person); // false
Person.prototype.isPrototypeOf(person); // true
Object.prototype.isPrototypeOf(person); // true

Employee.prototype.isPrototypeOf(employee); // true
Person.prototype.isPrototypeOf(employee); // true
Object.prototype.isPrototypeOf(employee); // true

Where the inheritance technique fails

Whilst the method of extending an object above works it does have an implicit requirement for the parent object’s constructor function to allow a parameterless constructor to work, which can be a problem in certain situations.

For instance, if the Person object’s constructor explicitly requires the parameters name and age to be specified:

function Person(name, age) {
    if (!name || !age) {
        throw new Error("A person must have a name and age");
    }

    // properties
    this.Name = name;
    this.Age = age;

    // methods
    this.sayHello = function () {
        return "Hello, my name is " + this.Name + "!";
    }
}

In situations like this, this line will except:

Employee.prototype = new Person(); // Uncaught Error: A person must have a name and age

Other inheritance techniques

Besides the technique I mentioned above, here are two other ways to create object inheritance for your reference.

Douglas Crockford’s initialize technique

In Douglas Crockford’s post here he wrote about a different technique on initializing objects, in his last update in 2008 this is his preferred method:

if (typeof Object.create !== 'function') {
    Object.create = function (o) {
        function F() { }
        F.prototype = o;
        return new F();
    };
}

and to use it, suppose we have already created an Employee object called employee, to create another just like it:

var employee2 = Object.create(employee);

This technique is more about cloning an existing object, employee2 will therefore inherit all of employee‘s current state:

image

MooTools

MooTools is a modular Object-Oriented Javascript framework, it includes a Class constructor function which includes some nice features such as:

  • Extends – allows you to specify a base class which to extend from
  • Implements – allows you to specify one or more classes to adopt properties from (think implementing an interface)

So using MooTools, this is how you can create the Person and Employee classes:

// create a new Person class
var Person = new Class({
    initialize: function (name, age) {
        // properties
        this.Name = name;
        this.Age = age;

        // methods
        this.sayHello = function () {
            return "Hello, my name is " + this.Name + "!";
        }
    }
});

// add the sayBye function to the Person class
Person.implement({
    sayBye: function (recipient) {
        return "Good bye, " + recipient + "!";
    }
});

// create a new Employee class which inherits from the Person class
var Employee = new Class({
    Extends: Person,
    initialize: function (name, age, salary) {
        this.parent(name, age); // calls initialize method of Person class
        this.Salary = salary;

        // new property and method
        this.Salary = salary;
        this.reportSalary = function () {
            return "Hi, my name is " + this.Name + ", my salary is " + this.Salary + ".";
        }
    }
});

var person = new Person("John", 10);
var employee = new Employee("Jane", 20, 12000);

This is how the person and employee objects look in the Chrome Debugger:

image

References:

Douglas Crockford on Prototypal Inheritance

MooTools Class examples


Whenever you’re ready, here are 3 ways I can help you:

  1. Production-Ready Serverless: Join 20+ AWS Heroes & Community Builders and 1000+ other students in levelling up your serverless game.
  2. Consulting: If you want to improve feature velocity, reduce costs, and make your systems more scalable, secure, and resilient, then let’s work together and make it happen.
  3. Join my FREE Community on Skool, where you can ask for help, share your success stories and hang out with me and other like-minded people without all the negativity from social media.

 

Leave a Comment

Your email address will not be published. Required fields are marked *