0

class Person {
  constructor(name) {
    this.name = name
  }

  method() {
    console.log(this.name)
  }
}

class Student extends Person {
  constructor(name) {
    super(name)
  }
}

class Teacher extends Person {
  constructor(name) {
    super(name)
  }
}

student = new Student("James");
student.method()

person = new Teacher("Alastair");
person.method()

I understand polymorphism is the idea an inherited method will do different behaviour depending on the instance it's in. Is .method a good example? What would be a better one?

tonitone120
  • 1,920
  • 3
  • 8
  • 25
  • 2
    `method` here is not polymorphism. It's just inherited. – Taplar Aug 11 '20 at 18:05
  • Related: https://stackoverflow.com/questions/27642239/what-is-polymorphism-in-javascript – Taplar Aug 11 '20 at 18:08
  • Polymorphism, in the traditional sense, is that you have multiple methods with the same name. In this case there is only a single method. – Taplar Aug 11 '20 at 18:10
  • @Taplar So if I had to generalise it, I would say: same-named method on two child classes (a same-named method also exists on parent class?), that perform similar function, but are slightly different? – tonitone120 Aug 11 '20 at 19:04
  • A construed example of such a case would be, say you had a class of `State` which held an object definition. And then you had an `XmlState` and `JsonState` which extended it, both with their own `toString` method. In that situation, both would be able to call `variable.toString()`, with different results, depending upon what implementation of the method they have. – Taplar Aug 11 '20 at 20:30

3 Answers3

1

This would be a polymorphism.

class Student extends Person {
  constructor(name) {
    super(name)
  }
  method() {
    console.log('Student ' + this.name)
  }
}

class Teacher extends Person {
  constructor(name) {
    super(name)
  }
  method() {
    console.log('Teacher ' + this.name)
  }
}
Hayden S.
  • 742
  • 4
  • 10
  • So if I had to generalise it, I would say: same-named method on two child classes (a same-named method also exists on parent class?), that perform similar function, but are slightly different? – tonitone120 Aug 11 '20 at 19:04
  • Yes, that's the polymorphism. – Hayden S. Aug 11 '20 at 19:13
1

In the example you've posted the method() method is inherited in the Teacher and Student classes, so it not polymorphic.

If you add a method() to the Teacher and/or Student sub-classes, then you have some classes that can behave slightly differently depending on the type that they are an instance of

for example

class Person {
  constructor(name) {
    this.name = name
  }

  method() {
    // I've moved the console.log (side effect) for sake of simplicity
    return this.name
  }
}

// no need to add constructor because is inherited from Person
// nor method, because it inherits method too
class Student extends Person {}

class Teacher extends Person {
   // inherits constructor...

  // override method decorating it
  method() {
       return super.method() + ", is a teacher"
  }
}

class Employee extends Person {
   static count = 0;
   
   // override constructor necessarily decorating it
   constructor(name){
      super(name)
      this.id = ++this.constructor.count;
   }

   // override method
   method(){
       return "employee " +this.id
   }
}
Employee.count = 0

// fill a list of different types of Person
var people = [
   new Student("James"),
   new Teacher("Alastair"),
   new Employee("Bob"),
]

// print all the instances
people.forEach(x => console.log(x.method()))

You also have to maintain the compatibility of the classes through the hierarchy (same or wider set of arguments, same or more well defined return type), this is known as the Liskov substitution principle.

IMO JS and ES6 are not the best languages for learning traditional OOP principles due to the prototype based inheritance and the weak type system that they have both.

In JS instead is very easy to compose different behavior (methods) when you make the instances, and then use polymorphism without inheritance, an easy way to achieve this could be the following example

class Person {
   constructor({name, ...tail}){
      if(!name){
         throw new TypeError("person.name is missing")
      }
      Object.assign(this, {name, ...tail})
   }
   method(){
      return this.name
   }
   greet(){ return "hi"}
}

class Robot {
   constructor(...args){
      Object.assign(this, ...args)
      if(this.constructor.prototype !== Object.getPrototypeOf(this)) {
          // throw if __proto__ pollution on the instance
          throw new TypeError("override __proto__")
      }

   }
    method(){
        return `a robot that says ${
            Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString("2")
        }`
    }
}

// the following code might seem unclear but in return
// it could be used with instances of different classes
function introduce(){
   return this.greet() +
          ", I'm " +
          this.constructor.prototype.method.apply(this, arguments)
}



// fill a list of different instances of Person
var people = [
   new Person({name: "James"}),
   new Person({
     name: "Alastair",
     method: introduce,
   }),
   new Person({
     name: "Bob",
     method: introduce,
     greet: () => "hola"
   }),
   new Robot({
     method: introduce,
     greet: () => "hello"
   })
]

// print all the instances
people.forEach(x => console.log(x.method()))

Anyway at the end of the day polymorphism is when you have different classes that shares the aspect but not the behavior so they have a method with the same name, possibly the same arguments and possibly the same return type, with a different implementation, and you can use them interchangeably.

Let's take a gun, a drill and and an electric screwdriver, they all share the aspect trigger but if you pull the trigger they all behave differently

asdru
  • 1,147
  • 10
  • 19
0

JavaScript embraces the prinicple that objects are instances of objects and not classes (derived from Self). Combined with Dynamic Type System it allows for "Duck Typing" which can give you (an approximation) subtype polymorphism (same method name and signature with different implementations in related objects) via (naming) convention (see @Hayden S. answer).

However you cannot get adhoc polymorphism (same method name with different signatures within a single object) without leveraging the Arguments object or spread operator (...args) and dyanamically interrogating the inputs, and then dispatching on your own. For example, dispatching to a specfic method for a string vs any other type:

someObject.method = function(...args)  {
  if (isString(args[0]) {
    this.doSomethingWithString.apply(this, args)
  }
  else 
  {
    this.doSomethingElse.apply(this, args)
  }
} 
akaphenom
  • 6,728
  • 10
  • 59
  • 109