For other translations of this series, see [JS Working Mechanism – Xiaobai’s 1991 column – Nuggets (juejin. Cn)] (juejin.cn/column/6988…

The concept of polymorphism is actually quite mature, but the author of this article seems to understand the polymorphism is not the same as static language polymorphism.

In the real world, a woman can have different roles. She can be a mother, a worker and a wife at the same time. Depending on the role, she needs to do different behaviors. That’s the idea of polymorphism, just in another form.

In JS, the concept of polymorphism is not well known, but in object-oriented programming, it is a very central concept.

Data-oriented programming languages such as Rust. Polymorphism is also implemented with Entity Component System (ECS). Programming in JS has different modes. In this topic, we will reveal what polymorphism is, how it is used in JS, and the different types of polymorphism.

What is polymorphism

If we want to calculate the area and perimeter of an area, we might define two methods area() and perimeter(). But it’s impossible to compute different shapes with one set of algorithms. For example, the formula for the circumference of a circle and a triangle are different. We define the base class, and then treat the different shapes as subclasses or derived classes with their own formulas for calculating the perimeter. This is called polymorphism. In programming, polymorphism is defined as the ability of an object to have multiple patterns. In the next section, we’ll take a closer look at how JS handles polymorphism.

How does JS handle polymorphism

Different languages implement polymorphism in different ways. For example, JS and JAVA are both object-oriented programming languages, but they also implement polymorphism in different ways. We will also see how polymorphism works in encapsulation and inheritance.

Polymorphism and object-oriented programming

The concepts of object-oriented model dependencies are objects and classes. Change its data field by this or self

We will compare how polymorphism is implemented in JS with other data-oriented programming (using ECS). Using object-oriented programming, we can create a class to calculate the area and perimeter of different shapes:

class Shape {
  area() {
    return 0;
  }
  perimeter() {
    return 0;
  }
  toString() {
    return Object.getPrototypeOf(this).constructor.name;
  }
}

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

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

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

class Rectangle extends Shape {
  constructor(w, h) {
    super();
    this.width = w;
    this.height = h;
  }

  area() {
    return this.width * this.height;
  }
  perimeter() {
    return 2 * (this.width + this.height);
  }
}

function cumulateShapes(shapes) {
  return shapes.reduce((sum, shape) => {
    if (shape instanceof Shape) {
      console.log(`Shape: ${shape.toString()} - area: ${shape.area()}`);
      console.log(
        `Shape: ${shape.toString()} - perimeter: ${shape.perimeter()}`
      );
      return sum + shape.area();
    }
    throw Error("Bad argument shape.");
  }, 0);
}

const shapes = [new Circle(3), new Rectangle(2, 3)];

console.log(cumulateShapes(shapes));
Copy the code

If you use ECS, it looks like this

var Position; var Circle; var Rectangle; class CirlceSystem extends Position { OnUpdate() { ComponentQuery.SelectReadOnly(typeof Position, typeof Circle).ForEachEntity( (Entity, Position, Circle) => { /* find area and perimeter */ } ); } } class RectangleSystem extends Position { OnUpdate() { ComponentQuery.SelectReadOnly( typeof Position, typeof Rectangle ).ForEachEntity((entity, Position, Rectangle) => { /* find area and perimeter */ }); }}Copy the code

In the JS code, we use inheritance. In the ECS code, we use the ECS model to distribute entities to components and decouple data.

Let’s take a closer look at inheritance in JS and how it relates to polymorphism

Polymorphism and inheritance

Inheritance is a very important feature in object-oriented polymorphism. Look at an example of car

class Car { constructor(color, speed) { this._speed = speed; this._color = color; }}Copy the code

Now we have different subclasses, like BMW, Toyota, Honda, etc., and they have different attributes like color and speed

class BMW extends Car { constructor(color, speed, make) { super(color, speed); this._make = make; } showInfo() {console.log("I'm "+ this._make + ", my color is" + this._color + ", and my speed is " + this._speed ); } } class Toyota extends Car { constructor(color, speed, make) { super(color, speed); this._make = make; } showInfo() {console.log("I'm "+ this._make + ", my color is" + this._color + ", and my speed is " + this._speed ); } } class Bentely extends Car { constructor(color, speed, make) { super(color, speed); this._make = make; } showInfo() {console.log("I'm "+ this._make + ", my color is" + this._color + ", and my speed is " + this._speed ); }}Copy the code

Now, let’s add different colors and speeds to our cars.

var myBentely = new Bentely('Red', '20mph', 'Bentely');
var myBMW = new BMW('Green', '40mph', 'BMW');
var myToyota = new Toyota('White', '60mph', 'Toyota');
console.log(myBentely.showInfo()); 
console.log(myBMW.showInfo());  
console.log(myToyota.showInfo());
Copy the code

In the example, the subclass takes the attribute of the parent class and defines it. Inheritance can be derived from the current parent or even a grandparent.

JS is an inherited type

JS inheritance is a big topic, and there are many different implementations, such as prototype-based, class-based (fake classes), and functional. Let’s take a quick look at the differences and how they implement polymorphism:

1. The prototype inheritance of the prototype is relatively simple, just add methods to the prototype

let Car = {
  color: "Red",
};
let BMW = {
  make: "BMW",
};

BMW.__proto__ = Car;

// we can find both properties in BMW now:
console.log("This is a " + BMW.color + " " + BMW.make);
Copy the code

2.  Class inheritance

Previously said, JS class is a pseudo-concept, syntax sugar, so we call it pseudo-class. The implementation of class is based on the new keyword, but calls a function. For example, we have a CAR object.

function Car(make, color, speed) {
  this.make = make;
  this.color = color;
  this.speed = speed;
}
Copy the code

We can use the new keyword to create different subclasses for it.

var Toyota = new Car ("Toyota", "Red", "100mph");
var Bentley = new Car ("Bentley", "White", "120mph");
var BMW = new Car ("BMW", "Green", "90mph");
Copy the code

Using the prototype, we created different CAR objects. Next, we’ll look at how we pass stereotypes like inheritance intentions and how this affects polymorphism. First, we create a dialogue function that our card inherits from.

function dialogue() {
  console.log('I am ' + this.make);
}
Copy the code

Take the prototype and let our cars inherit it

Car.prototype.dialogue = function () { console.log( "I am a " + this.color + " " + this.make + " with " + this.speed + "  speed " ); }; console.log(Toyota.dialogue()); console.log(BMW.dialogue()); console.log(Bentley.dialogue());Copy the code

3. Function-based inheritance

Function-based inheritance is to add enhancement functions to an object

function Person(data) {
  var that = {};
  that.name = data.name;
  return that;
}

// Create a child object, to inherit from the base Person
function Employee(data) {
  var that = Person(data);
  that.sayHello = function () {
    return "Hello, I'm " + that.name;
  };
  return that;
}

var myEmployee = Employee({ name: "Rufi" });
console.log(myEmployee.sayHello());
Copy the code

Polymorphism and encapsulation

Once you understand inheritance, it’s easy to understand encapsulation. When writing code, we often need to wrap some code so that the user cannot access the values inside from the outside.

For example, we combine data that verifies student characteristics and then inherit it using a prototype-based polymorphism approach.

function Student(name, marks) { var student_name = name; var student_marks = marks; Object.defineProperty(this, "name", { get: function () { return student_name; }, set: function (student_name) { this.student_name = student_name; }}); Object.defineProperty(this, "marks", { get: function () { return student_marks; }, set: function (student_marks) { this.student_marks = student_marks; }}); } var stud = new Student("Mercy's score is: ", 60); console.log(stud.name + " " + stud.marks);Copy the code

This example is a good way to understand encapsulation and polymorphism in JS. Many people don’t understand the difference between abstraction and encapsulation. Abstract, only part of the information is visible, the rest is hidden. Encapsulation, on the other hand, takes a packet into a single entity that is inaccessible to the outside world. The primary reason to use encapsulation is to control and validate data — as in the example above.

The type of polymorphism

There are many ways to implement polymorphism in JS, and we will discuss the following ones

Ad-hoc Polymorphism

AD hoc polymorphism refers to ‘visual’ types that behave differently. AD hoc polymorphisms can contain functions of the same name, but with different arguments or return values.

This type is also called overloading, so let’s look at overloading an operator.

Operator Overloading

5 + 5; // will print 10
 'I am' + ' ' + '5 years old' // will print I am 5 years old
Copy the code

In the example above, + represents the two paradigms of number addition and string concatenation.

Parameterized polymorphism

Parameterized polymorphism handles common functions and data types while maintaining static type safety. Common functions and data types can be overridden, so they are not treated differently based on their type. For example, objects hold different data types. It does not distinguish between their values based on their type.

const Ann = {
firstName: 'Ann',
lastName: 'Kim',
Age: 4,
Adult: false
}
Copy the code

The Ann object above contains Ann’s name – string type, age – number type, adult – Boolean type. Although they differ in type, the way objects are handled is similar. A similar example is arrays. In JS, arrays can hold different elements. Const Ann = [‘ Ann ‘, ‘Kim’, 4, false];

An array handles its elements similarly. If you run console.log(Ann) on the console, you’ll see that all elements are printed out.

Let’s do another example

const id = (x) => x;
id(1); // 1
id("foo"); // "foo"
Copy the code

Id does not judge the values of parameters 1 and foo based on their types. So you can pass in different types of arguments to id.

Subtype polymorphism

Subtype polymorphism contains subtypes and subtype data types. It will not include the creation of new objects, mainly interface-based implementations, and different implementations.

Suppose you get a family member’s bequest – a bookstore. So you can look up the books, look up the estate accounts, look up the bookstore customers and so on, and that’s called inheritance, you get everything in the estate.

If the family legacy is not given to you, you can choose to start a new one yourself, and take on the role of your relative, but change it as you like – this is called the type.

Look at an example

function Animal () {

}

Animal cat = new Cat ("Kitty");
Animal Dog = new Cat ("puppy");
Animal cat = new Cat ("Kiddy");

//you can go ahead to create different properties for different animals
Copy the code

Cat,dog and goat are all subtypes of animals. An animal can be any animal. You can do different implementations for different animals.

Common JS polymorphic traps

We’ve covered polymorphism in general, but there are some pitfalls to keep in mind:

  1. Polymorphism can affect the performance of your code. A single function runs faster than a polymorphic function.
  2. Polymorphism reduces the readability of code. To solve this problem, write comments when using polymorphism.
  3. It’s easy to implement polymorphism in JS, but understand inheritance. Because polymorphism in JS is implemented around inheritance.

Why polymorphism

For reuse. On the one hand, the ability to reuse code is improved by using inheritance. On the other hand, different types of data can be processed together. Take the familiar array.

Const Ann = [' Ann ', 'Kim', 4, false];Copy the code

The use of polymorphism in the program, the most important or make the program scalability and maintainability better.