In-Depth Understanding of the Underlying Principles of Classes in ES6
- 621Words
- 3Minutes
- 13 Sep, 2024
ES6 introduced the class
feature, making JavaScript’s object-oriented programming style more concise and easier to understand. Although classes resemble the classical object-oriented model of other programming languages, their underlying mechanism is still JavaScript’s existing prototype-based inheritance. This article delves into the underlying principles of ES6 classes to help you better understand how they work.
1. The Essence of Classes is Constructors
A class defined in ES6 is essentially a constructor. You can verify the nature of a class using the typeof
keyword:
1class Person {2 constructor(name) {3 this.name = name;4 }5}6
7console.log(typeof Person); // "function"
The class definition is ultimately compiled into a function, meaning that a class is just syntactic sugar for a constructor.
2. How Constructors Work
The constructor
method in a class is used to initialize class instances. When creating an instance of a class, the new
keyword calls this constructor:
1class Person {2 constructor(name) {3 this.name = name;4 }5}6
7const p = new Person("Alice");8console.log(p.name); // "Alice"
This is very similar to traditional ES5 function constructors:
1function Person(name) {2 this.name = name;3}4
5const p = new Person("Alice");6console.log(p.name); // "Alice"
3. Prototype Inheritance
The methods within a class are defined on its prototype, which is entirely consistent with the ES5 prototype inheritance mechanism. Whenever a method is defined in a class, it is actually added to the class’s prototype:
1class Person {2 constructor(name) {3 this.name = name;4 }5
6 greet() {7 console.log(`Hello, ${this.name}!`);8 }9}10
11const p = new Person("Alice");12p.greet(); // "Hello, Alice!"
The same result can be achieved in ES5 as follows:
1function Person(name) {2 this.name = name;3}4
5Person.prototype.greet = function () {6 console.log(`Hello, ${this.name}!`);7};8
9const p = new Person("Alice");10p.greet(); // "Hello, Alice!"
The underlying inheritance mechanism of classes is still implemented through the prototype chain.
4. Class Inheritance Mechanism
ES6 provides the extends
keyword to support class inheritance, which relies on JavaScript’s existing prototype-based inheritance. When a subclass inherits from a parent class, the subclass’s prototype points to the parent class’s prototype, thus achieving method inheritance:
1class Animal {2 constructor(name) {3 this.name = name;4 }5
6 speak() {7 console.log(`${this.name} makes a noise.`);8 }9}10
11class Dog extends Animal {12 speak() {13 console.log(`${this.name} barks.`);14 }15}16
17const d = new Dog("Rex");18d.speak(); // "Rex barks."
This is equivalent to manually implementing inheritance in ES5 using the prototype chain:
1function Animal(name) {2 this.name = name;3}4
5Animal.prototype.speak = function () {6 console.log(`${this.name} makes a noise.`);7};8
9function Dog(name) {10 Animal.call(this, name);11}12
13Dog.prototype = Object.create(Animal.prototype);14Dog.prototype.constructor = Dog;15
16Dog.prototype.speak = function () {17 console.log(`${this.name} barks.`);18};19
20const d = new Dog("Rex");21d.speak(); // "Rex barks."
5. Using super
to Call Parent Class Methods
The super
keyword allows a subclass to call the parent class’s constructor and methods. In the subclass constructor, super
is used to call the parent class’s constructor, inheriting the parent’s properties and methods:
1class Animal {2 constructor(name) {3 this.name = name;4 }5}6
7class Dog extends Animal {8 constructor(name, breed) {9 super(name); // Call the parent class's constructor10 this.breed = breed;11 }12}13
14const d = new Dog("Rex", "Labrador");15console.log(d.name); // "Rex"16console.log(d.breed); // "Labrador"
In ES5, a similar effect can be achieved by explicitly calling the parent class constructor:
1function Animal(name) {2 this.name = name;3}4
5function Dog(name, breed) {6 Animal.call(this, name); // Call the parent class's constructor7 this.breed = breed;8}9
10const d = new Dog("Rex", "Labrador");11console.log(d.name); // "Rex"12console.log(d.breed); // "Labrador"
Summary
The introduction of classes in ES6 provides a more concise syntax for object-oriented programming in JavaScript, but its underlying mechanics still rely on constructors and the prototype chain. Classes are essentially syntactic sugar for constructors, and class inheritance is based on the prototype chain. With super
, subclasses can call the parent class’s constructor and methods, achieving inheritance. Understanding the underlying principles of classes helps us better utilize JavaScript’s object-oriented features in development.