We went over classes in the last blog post and how they make it easier to create objects using a blueprint. The class keyword was introduced in ES2015/ES6, and there is common misconception that classes are barely syntactic sugar and nothing more. Classes are the core fundamentals of object-oriented programming. My aim with this blog post is to demystify the misconception and showcase how classes are slightly different from functions paired with new keyword.

What does a class do?

class EmployeeRecord {
    name = "New User";
    id = 0;

    constructor(firstName, lastName, id) {
        this.name = `${firstName} ${lastName}`;
        this.id = id;
    }

    reverseName() {
        return this.name.split("").reverse().join("");
    }
}

const employee1 = new EmployeeRecord("Parwinder", "Bhagat", 1);
const employee2 = new EmployeeRecord("Lauren", "L", 2);

console.log(employee1.name); // Parwinder Bhagat
console.log(employee2.name); // Lauren L
console.log(employee1.reverseName()); // tagahB redniwraP

In the above class example:

  1. Under the hood, a function called EmployeeRecord is created. The function body is made of the constructor of the class. If there is no constructor, then the function body is empty.
  2. All the class methods are stored on the prototype of EmployeeRecord.

With that logic, we can re-write the above class without using classes or the class keyword.

function EmployeeRecord(firstName, lastName, id) {
    this.name = `${firstName} ${lastName}`;
    this.id = id;
}

EmployeeRecord.prototype.reverseName = function () {
    return this.name.split("").reverse().join("");
}

let employee1 = new EmployeeRecord("Parwinder", "Bhagat", 1);
const employee2 = new EmployeeRecord("Lauren", "L", 2);

console.log(employee1.name); // Parwinder Bhagat
console.log(employee2.name); // Lauren L
console.log(employee1.reverseName()); // tagahB redniwraP

The results are the same and this is where class is just syntactic sugar comes from.

How do classes differ?

  • There is a specific function kind assigned to classes and it is checked at multiple places most importantly whenever we instantiate a class.
class EmployeeRecord {
    constructor() { }
}

console.log(typeof EmployeeRecord); // function
EmployeeRecord(); // Value of type 'typeof EmployeeRecord' is not callable. Did you mean to include 'new'?
  • Functional inheritance works using the prototype. Classes do the same using a cleaner syntax with extends keyword.
class Person {
    sayName() {
        console.log("My name is Person");
    }

    sayAge() {
        console.log("I am 30 years old."); // I am 30 years old.
    }
}

class Employee extends Person {
    sayDepartment() {
        console.log("I work for the tech department."); // I work for the tech department.
    }

    sayHello() {
        console.log("Hi, I am the new employee"); // Hi, I am the new employee
    }
}

let employee = new Employee;

employee.sayHello();
employee.sayAge();
employee.sayDepartment();

console.log(employee instanceof Person); // true
console.log(employee instanceof Employee); // true
  • Function declarations are hoisted, and class declarations are not!
const employee = new Employee(); // ReferenceError or Employee is not a constructor

class Employee {
    constructor() {}
}
  • A class always runs in strict mode. All code inside the class is automatically in strict mode.

  • Function decelerations and expressions can be overridden as they are similar to a var whereas classes are not overridden. They are like let and const keywords, let does not allow multiple declarations with the same name inside its scope.

  • Objects can have (non-enumerable) properties that do not show up when iterated through that object. Class methods are non-enumerable and have the enumerable property set to false. If we use for..in to loop over an object from a class, we will not get the methods.