What are the differences between JavaScript ES2015 classes and ES5 function constructors?

Answer

In JavaScript, both ES5 function constructors and ES2015 classes are used to create objects and handle prototypes. However, ES2015 classes introduced a more structured and cleaner syntax for defining object-oriented logic compared to the ES5 function constructor approach.

  1. Syntax and Readability

Example:

function Person(name, age) {
  this.name = name;
  this.age = age;
}

Person.prototype.sayHello = function () {
  console.log(`Hello, I am ${this.name}.`);
};

const person1 = new Person('Alice', 25);
person1.sayHello(); // Hello, I am Alice.

Example:

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  sayHello() {
    console.log(`Hello, I am ${this.name}.`);
  }
}

const person1 = new Person('Alice', 25);
person1.sayHello(); // Hello, I am Alice.

Key Difference: The class syntax is more readable and avoids manual prototype manipulation.

  1. class is Syntactic Sugar

Example of Equivalent Function Constructor for a Class:

class ExampleClass {
  method() {}
}

// Equivalent ES5 code:
function ExampleClass() {}
ExampleClass.prototype.method = function () {};
  1. Strict Mode

Example:

class Test {
  constructor() {
    undeclaredVariable = 5; // ReferenceError in ES2015 class
  }
}

new Test();
  1. Behavior of this

Example:

// ES5 Constructor
function Person(name) {
  this.name = name;
}
const p1 = Person('Alice'); // No "new" - this refers to global object
console.log(p1); // undefined

// ES2015 Class
class PersonClass {
  constructor(name) {
    this.name = name;
  }
}
const p2 = PersonClass('Alice'); // TypeError: Class constructor cannot be invoked without 'new'
  1. Hoisting

Example:

// ES5
const obj1 = new Person();
function Person() {}

// ES2015
const obj2 = new PersonClass(); // ReferenceError
class PersonClass {}
  1. Inheritance

ES5 Example:

function Animal(name) {
  this.name = name;
}
Animal.prototype.speak = function () {
  console.log(`${this.name} makes a sound.`);
};

function Dog(name, breed) {
  Animal.call(this, name);
  this.breed = breed;
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

const dog = new Dog('Buddy', 'Golden Retriever');
dog.speak(); // Buddy makes a sound.

ES2015 Class Example:

class Animal {
  constructor(name) {
    this.name = name;
  }
  speak() {
    console.log(`${this.name} makes a sound.`);
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }
}

const dog = new Dog('Buddy', 'Golden Retriever');
dog.speak(); // Buddy makes a sound.

Key Difference: ES2015 extends and super make inheritance much cleaner and easier to implement.

  1. [[FunctionKind]] Internal Property

Conclusion

While ES5 function constructors and ES2015 classes achieve similar results under the hood, ES2015 classes offer a cleaner and more structured syntax, making code easier to write and maintain. Additionally, classes simplify inheritance and enforce stricter behavior to reduce bugs.

MDN Web Docs: Classes

MDN Web Docs - Function.prototype