Chapter 9: Classes
9.1 Class basic syntax
The “class” syntax
class MyClass {
  prop = value; // property
  constructor(...) { // constructor
    // ...
  }
  method(...) {} // method
  get something(...) {} // getter method
  set something(...) {} // setter method
  [Symbol.iterator]() {} // method with computed name (symbol here)
  // ...
}The constructor() method is called automatically by new, so we can initialize the object there. There's no comma between class methods.
class User {
  constructor(name) {
    this.name = name;
  }
  sayHi() {
    alert(this.name);
  }
}What is a class?
In JavaScript, a class is a kind of function. What class User {...} construct really does is:
- Creates a function named - User, that becomes the result of the class declaration. The function code is taken from the- constructormethod (assumed empty if we don’t write such method).
- Stores class methods, such as - sayHi, in- User.prototype.
Not just a syntactic sugar
- A function created by class is labelled by a special internal property - [[FunctionKind]]:"classConstructor".
- Unlike a regular function, a class must be called with - new.
- Class methods are non-enumerable. A class definition sets - enumerableflag to- falsefor all methods in the- prototype.
- All code inside the class construct is automatically in strict mode. 
Class Expression
let User = class Name {
  sayHi() {
    alert("Hello");
  }
};Dynamically make a class on-demand:
function makeClass(phrase) {
  // declare a class and return it
  return class {
    sayHi() {
      alert(phrase);
    };
  };
}Getters/setters
classes may include getters/setters, computed properties etc.
Such class declaration works by creating getters and setters in the prototype.
Computed names […]
class User {
  ['say' + 'Hi']() {
    alert("Hello");
  }
}Class fields
"Class fields" is a syntax that allows to add any properties. They are set on individual objects, not the prototype.
class User {
  name = "John";
  promptName = prompt("Name, please?", "John");
  sayHi() {
    alert(`Hello, ${this.name}!`);
  }
}Making bound methods with class fields
Instead of using a wrapper-function or bind the method to object, we can use arrow functions to solve the "losing this" problem. The function is created on a per-object basis.
class Button {
  constructor(value) {
    this.value = value;
  }
  click = () => {
    alert(this.value);
  }
}9.2 Class inheritance
The “extends” keyword
The syntax to extend another class is: class Child extends Parent. Any expression is allowed after extends.
class Rabbit extends Animal {
  hide() {
    alert(`${this.name} hides!`);
  }
}Internally, extends keyword works using the good old prototype mechanics. It sets Rabbit.prototype.[[Prototype]] to Animal.prototype.
Overriding a method
Usually we don’t want to totally replace a parent method, but rather to build on top of it to tweak or extend its functionality.
- super.method(...)to call a parent method.
- super(...)to call a parent constructor (inside our constructor only).
Arrow functions have no super. It’s taken from the outer function.
Overriding constructor
If a class extends another class and has no constructor, then the following “empty” constructor is generated:
class Rabbit extends Animal {
  constructor(...args) {
    super(...args);
  }
}Constructors in inheriting classes must call super(...), and do it before using this.
Explain: A derived constructor has a special internal property [[ConstructorKind]]:"derived".
- When a regular function is executed with new, it creates an empty object and assigns it to - this.
- When a derived constructor runs, it expects the parent constructor to do the job. 
Overriding class fields
Here's the tricky part:
class Animal {
  name = 'animal'
  constructor() {
    alert(this.name); // (*)
  }
}
class Rabbit extends Animal {
  name = 'rabbit';
}
new Animal(); // animal
new Rabbit(); // animalThere’s no own constructor in Rabbit, so Animal constructor is called. Parent constructor always uses its own field value, not the overridden one.
Explain: The class field is initialized:
- Before constructor for the base class (that doesn’t extend anything). 
- Imediately after - super()for the derived class.
So that when the Rabbit constructor implicity calls super(), its class field has not been initialized yet, and that’s why the parent fields are used.
Super: internals, [[HomeObject]]
[[HomeObject]]
When a function is specified as a class or object method, its [[HomeObject]] property becomes that object. When super() is called, it takes the parent method from the prototype of its [[HomeObject]], without using this.
Methods are not “free”
[[HomeObject]] can’t be changed, so this bond is forever. If a method does not use super, then we can still consider it free and copy between objects. But with super things may go wrong.
Methods, not function properties
[[HomeObject]] is defined for methods both in classes and in plain objects. For objects, methods must be specified exactly as method(), not as "method: function()".
9.3 Static properties and methods
We can also assign a method to the class function itself, not to its prototype.
class User {
  static staticMethod() {
    alert(this === User);
  }
}9.4 Private and protected properties and methods
It’s always convenient when implementation details are hidden, and a simple, well-documented external interface is available.
Internal and external interface
- Public: accessible from anywhere. They comprise the external interface. 
- Private: accessible only from inside the class. These are for the internal interface. 
Protected properties
Protected properties are usually prefixed with an underscore _. That is not enforced on the language level, but there’s a well-known convention.
Protected fields are inherited.
Private properties
The recent JavaScript proposal provides language-level support for private properties and methods.
- Privates should start with - #.
- They are only accessible from inside the class. 
- We can’t access them from outside or from inheriting classes. 
- Private fields do not conflict with public ones. 
- Private fields are not available as - this[name].
9.5 Extending built-in classes
Built-in classes like Array, Map and others are extendable.
class PowerArray extends Array {
  isEmpty() {
    return this.length === 0;
  }
}Built-in methods like filter, map and others return new objects of exactly the inherited type PowerArray. Their internal implementation uses the object’s constructor property for that.
We can add a special static getter Symbol.species to the class. If it exists, it should return the constructor that JavaScript will use internally to create new entities in map, filter and so on.
There's no static inheritance in built-ins.
9.6 Class checking: "instanceof"
The instanceof operator
obj instanceof ClassIt returns true if obj belongs to the Class or a class inheriting from it.
Normally, instanceof examines the prototype chain for the check. We can also set a custom logic in the static method Symbol.hasInstance.
Workflow of "instanceof"
- If there’s a static method - Symbol.hasInstance, then just call it:- Class[Symbol.hasInstance](obj).
- Checks whether - Class.prototypeis equal to one of the prototypes in the- objprototype chain.
Symbol.toStringTag
The behavior of Object toString can be customized using a special object property Symbol.toStringTag.
let user = {
};
alert({}.toString.call(user)); // [object User]9.7 Mixins
Mixin: a class that contains methods for other classes. JavaScript does not support multiple inheritance, but mixins can be implemented by copying methods into prototype.
A mixin example
// mixin
let sayHiMixin = {
  sayHi() {
    alert(`Hello ${this.name}`);
  },
  sayBye() {
    alert(`Bye ${this.name}`);
  }
};
// usage:
class User extends Person {
  constructor(name) {
    this.name = name;
  }
}
// copy the methods
Object.assign(User.prototype, sayHiMixin);Mixins can also make use of inheritance inside themselves. super looks for parent methods in [[HomeObject]].[[Prototype]].
Last updated