What I learned from The Principles of Object Oriented Javascript by Nicholas Zakas

Sun May 22 2016

ES6 classes in javascript is just syntactic sugar. Under the hood, prototyping is used to achieve a class so that it behaves the same way as built-in classes do in other programming languages.

I wanted to be more in depth with my javascript knowledege. I have used ES6 classes and inheritance in my projects but I've never understood how it works natively through prototying. I've read the book The Principles of Object Oriented Javascript in order fill this gap in my knowledge. Here's a list of things that I learned from the book.

  1. Javascript has two types: Primitive and Reference. There are five primitive types: boolean, number, string, null and undefined. The variable with primitive types directly holds its value, not referencing or pointing the value to another object. Reference types are called objects in javascript, and they hold properties. Examples of reference types are functions, arrays, date, regex.

  2. In identifying reference types, we can use

    const greet = () => { return "hello" } 
    console.log(typeof greet); // function 

    However, for the other reference types such as RegExp, Arrays, etc. we can use instanceof

    const people = ['Mark', 'John']  
    console.log(people instance of Array) // true
  3. Aside from primitive types, everything else in Javascript are objects! Even functions are objects. They behave, and can be modified exactly like objects. Objects are reference types, so you can store or refer an object within an object. In that case, you can also store a function inside a function. Functions are just a little different from other object types because they have inherent properties call(), apply() and bind().

  4. Parameters passed to a function are stored in array called arguments.

    function greet() { 
      Object.keys(arguments).forEach(key => { 
    		      console.log('Hi ', arguments[key]) ; 
    		    }) 
    } 
    
    greet('Mark', 'John') // Hi Mark; Hi John

    Function overloading can be achieved using the arguments property.

    function greet() {
     if (arguments.length === 0) {
       console.log('Hello there');
     }
     else {
       Object.keys(arguments).forEach(key => {
         console.log(arguments[key]);
       });
     }
    }
    
    greet(); // Hello there;
  5. We know that objects contains properties. There are two types of properties: data and accessor. Data properties directly store values. Accessor properties use getters and setters to perform actions. These properties have two internal attributes: enumerable and configurable. The enumerable attribute tells if a property can be iterated (which means it can be looped through a for loop). The configurable attribute tells if a property can be changed or modified. On default, all properties are set to be enumerable and configurable. There is another attribute that only exists on data properties. It is the writeable attribute and it tells if a properties value can be changed or modified.

     const dude = {
       name: 'Mark'
     }
     console.log(dude.propertyIsEnumerable('name')); // true 

    You can change the attribute of a property using the Object.defineProperty() function

     Object.defineProperty(dude, 'name', {
       enumarable: false
     })
     console.log(dude.propertyIsEnumerable('name')); // false
  6. Constructors in javascript are just functions that are called with new.

     function Model() {
     }
     const x = new Model();
     console.log(x instanceof Model); // true

    An object will always receive a constructor property referencing to the constructor function that called it.

    const person = new Person();
    console.log(person.constructor.name) // Person

    All objects inherit an Object.prototype property, which is the prototype property for objects. There are other built in prototypes as well for arrays, date, etc. The Object.prototype sits on top of the so called prototype chain. When a method is called, it starts to check names of functions of the instantiated object. When it cannot find the function, it goes up to the prototype chain. Here is an example:

    function Person(name) {
      this.name = 'name'
    }
    const Mark = new Person('Mark');

    It uses the function from Object.prototype, which is the top level prototype.

    Mark.toString() // [Object, object]

    Let's override the toString function on Person.prototype

    Person.prototype.toString = function() {
      console.log('Lorem ipsum')
    }

    Since the Person.prototype already has a method toString, it didn't need to search the higher prototypes in the prototype chain.

    Mark.toString() // Lorem ipsum

    Let's override the toString method on Mark's property directly

    Mark.toString = function() {
      console.log('Yada')
    }
    Mark.toString() // Yada

    When calling a method, the methods inside the objects are searched first, then it's prototype property methods, and its higher level prototypes and it will keep searching until it returns a null or it finds the method. Let's see what will happen when we instantiate a new person in our example.

    const John = new Person('John')
    John.toString() // Lorem ipsum

    Because there is no method toString in the John object, it runs the toString method from the inhereted Person.prototype property. And that is how inheretance is simulated through prototyping in javascript.

  7. There are no classes in javascript, but we can still have inheritance. Inheritance is done through assigning the prototype property of the "parent" object to the prototype property of the "child" object. All the methods that the parent prototype has, will be referenced by the child prototype

  8. One cool pattern I learned is the mixin pattern. I feel like it behaves the same way as inheritance, where it copies the properties of the parent to the child. The mixin pattern uses a function that accepts two parameters: the receiver and the supplier. The receiver is given with all the properties that the supplier has.

    function mixin(supplier, receiver) {
      for (let property in receiver) {
        supplier[property] = receiver[property];
      }
    
      return supplier;
    }

    There are more golden nuggets that can be taken from the book, so I'd recommend you to give it a read if you want to understand how to apply OOP in javascript.

Hi there 👋, I write about indie-hacking, tech, etc.If this interests you, consider subscribing to my newsletter below.