2

I'm familiar with prototypes quite well and now I try to understand how classes work by testing how they look like under the hood when compiled and compare it with 'prototypal' approach.

When I use prototypes, I can "extend" one object to another with Object.setPrototypeOf method. I pass as the second argument anything that is of [Object] type and it works, but when I use extends keyword with class, it expects only another [class] type object.

This is the working prototype-like approach:

function Citizen(country){
  this.country = country;
}

function Student(subject){
  this.subject = subject;
}


Object.setPrototypeOf(Student.prototype,new Citizen('Poland'));

var student = new Student('IT');
console.log(student.subject); //IT
console.log(student.country); //Poland

So, I "extends" the Student onto Citizen instance rather than Citizen itself (constructor). And it works just fine. I have the access to the properties of Citizen instance (student.contry).

How can I get the same result with the class and extends? I want to achieve something like in the code below, but it throws error: Class extends value #<Citizen> is not a constructor or null, what seems to be not as flexible as the use of pure prototypes.

class Citizen {
  constructor(subject){
    this.subject = subject;
  }
}

class Student extends new Citizen('USA') { 
  //this expects CLASS rather than instance of the class
  //Error: Class extends value #<Citizen> is not a constructor or null
  constructor(subject){
    this.subject = subject;
  }
}

var student = new Student('law');

console.log(student.subject); //law
Paweł
  • 4,238
  • 4
  • 21
  • 40
  • "*And it works just fine.*" - [No, it doesn't](https://stackoverflow.com/questions/12592913/what-is-the-reason-to-use-the-new-keyword-here) – Bergi Nov 08 '17 at 18:48
  • Do not use `Object.setPrototypeOf` for this. Not sure where you got that from. – Bergi Nov 08 '17 at 18:48
  • 2
    "*`class` seems to be not as flexible as the use of pure prototypes.*" - it never was meant to. Classes provide a relatively rigid framework that is hard to mess up. – Bergi Nov 08 '17 at 18:50
  • @Bergi from mdn. and why? – Paweł Nov 08 '17 at 19:33
  • [MDN itself warns you against it](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf). And [for good reason](https://stackoverflow.com/q/23807805/1048572). – Bergi Nov 08 '17 at 19:46
  • @Bergi Golden rule of performance: Always measure. http://jsbench.github.io/#6b02ea20162b69c083f39686b7a38b97 Performance seems OK. MDN should be updated. – Jeff M Nov 08 '17 at 19:57
  • @Bergi, is that mean that `extends` for the `class` is also discouraged as it works similarly under the hood? – Paweł Nov 08 '17 at 20:00
  • @JeffM Instantiation is not where the problem is. (Although in this particular case `setPrototypeOf` is not a problem indeed). Still, there's no good reason to prefer it over `Object.create` here. – Bergi Nov 08 '17 at 20:01
  • @Bergi When you use setPrototypeOf then you don't have to fix the constructor property. – Jeff M Nov 08 '17 at 20:02
  • 1
    @Paweł No, `extends` is fine - it's more akin to `Object.create` than to `setPrototypeOf`. – Bergi Nov 08 '17 at 20:02

1 Answers1

1

How can I get the same result with the class and extends?

You can't (-ish). But more importantly, you shouldn't. Even with the prototype approach, you still shouldn't. Your prototype approach should look like this:

Object.setPrototypeOf(Student.prototype, Citizen.prototype);

And likewise, your class approach should look like this:

class Student extends Citizen

And if for some reason you want your Student to have a hardcoded country, then you'd do that like this:

class Student extends Citizen {
    constructor(subject) {
        super("USA");
        this.subject = subject;
    }
}
Jeff M
  • 2,492
  • 3
  • 22
  • 38