JavaScript > Prototypes and Inheritance > Constructor Functions and Classes > ES6 Classes

ES6 Class: Shape and Circle Inheritance

This example demonstrates how to define classes using ES6 syntax and implement inheritance. We'll create a Shape class and a Circle class that inherits from it.

Basic ES6 Class Definition

This code defines a Shape class with a constructor that takes a color argument and initializes the color property. It also defines a draw method and a getColor method.

class Shape {
  constructor(color) {
    this.color = color;
  }

  draw() {
    console.log('Drawing a shape');
  }

  getColor() {
    return this.color;
  }
}

Class Inheritance

This code defines a Circle class that inherits from the Shape class using the extends keyword. The constructor calls the super() method to invoke the parent class's constructor. It also overrides the draw method and adds a new method, getArea.

class Circle extends Shape {
  constructor(color, radius) {
    super(color);
    this.radius = radius;
  }

  draw() {
    console.log('Drawing a circle');
  }

  getArea() {
    return Math.PI * this.radius * this.radius;
  }
}

Creating Instances

This code creates instances of both the Shape and Circle classes. Note how the Circle instance can access both the draw method (overridden) and the getColor method (inherited) from the Shape class, and its own getArea method.

const myShape = new Shape('red');
myShape.draw(); // Output: Drawing a shape
console.log(myShape.getColor()); // Output: red

const myCircle = new Circle('blue', 5);
myCircle.draw(); // Output: Drawing a circle
console.log(myCircle.getArea()); // Output: 78.53981633974483
console.log(myCircle.getColor()); // Output: blue

Concepts Behind the Snippet

This snippet illustrates key OOP (Object-Oriented Programming) concepts:

  • Encapsulation: The Shape and Circle classes encapsulate data (color, radius) and methods (draw, getColor, getArea) that operate on that data.
  • Inheritance: The Circle class inherits properties and methods from the Shape class, promoting code reuse and establishing an 'is-a' relationship (a Circle is a Shape).
  • Polymorphism: The draw method is present in both the Shape and Circle classes, but it behaves differently depending on the object it's called on. This is polymorphism in action.

Real-Life Use Case

Imagine building a game engine. You might have a base class like GameObject with properties like position and methods like render. Then, you can create derived classes like Player, Enemy, and Projectile that inherit from GameObject and add their own specific properties and methods. This promotes a well-structured and maintainable codebase.

Best Practices

  • Keep classes small and focused: Each class should have a clear and specific responsibility.
  • Use inheritance wisely: Don't overuse inheritance. Favor composition over inheritance when appropriate.
  • Follow the Single Responsibility Principle: Each class should have only one reason to change.
  • Document your classes: Use JSDoc or similar tools to document your classes and methods.

Interview Tip

Be prepared to explain the difference between classical inheritance (which ES6 classes simulate) and prototypal inheritance (which is the underlying mechanism in JavaScript). Understand the role of super() and how it's used to call the parent class's constructor.

When to Use Them

Use ES6 classes when you want to organize your code in an object-oriented manner, especially when dealing with complex applications with many related objects. Classes provide a more structured and readable syntax compared to traditional prototypal inheritance.

Memory Footprint

ES6 classes are essentially syntactic sugar over prototypal inheritance. Therefore, the memory footprint is comparable to using prototypal inheritance directly. The methods are added to the prototype of the class, which is shared among all instances, minimizing memory usage.

Alternatives

  • Prototypal Inheritance: The traditional way of achieving inheritance in JavaScript.
  • Factory Functions: Functions that return objects. They can be used to create objects with private state.
  • Composition: Creating objects by combining other objects, rather than inheriting from them.

Pros

  • Improved Readability: ES6 classes offer a cleaner and more familiar syntax for developers coming from other object-oriented languages.
  • Encapsulation: Classes provide better encapsulation compared to traditional prototypal inheritance (though JavaScript doesn't have true private members by default).
  • Code Organization: Classes help organize code into logical units, making it easier to maintain and reason about.

Cons

  • Syntactic Sugar: ES6 classes are just syntactic sugar over prototypal inheritance. They don't fundamentally change how inheritance works in JavaScript.
  • No True Private Members: JavaScript doesn't have true private members in classes (until recently with class fields), so it's possible to access and modify internal properties from outside the class. (though you can use the # prefix on properties and methods to make them private.)

FAQ

  • What is the purpose of the super() method?

    The super() method is used in the constructor of a derived class to call the constructor of the parent class. It's essential to initialize the parent class's properties before accessing this in the derived class's constructor.
  • Can I add methods to a class after it's been defined?

    Yes, you can add methods to a class after it's been defined by modifying its prototype. For example: Shape.prototype.printColor = function() { console.log(this.color); };