Chapter 9: Classes
9.1 Class basic syntax
The “class” syntax
The constructor()
method is called automatically by new
, so we can initialize the object there. There's no comma between class methods.
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 theconstructor
method (assumed empty if we don’t write such method).Stores class methods, such as
sayHi
, inUser.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
enumerable
flag tofalse
for all methods in theprototype
.All code inside the class construct is automatically in strict mode.
Class Expression
Dynamically make a class on-demand:
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 fields
"Class fields" is a syntax that allows to add any properties. They are set on individual objects, not the prototype
.
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.
9.2 Class inheritance
The “extends” keyword
The syntax to extend another class is: class Child extends Parent
. Any expression is allowed after extends
.
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:
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:
There’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
.
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.
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
It 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.prototype
is equal to one of the prototypes in theobj
prototype chain.
Symbol.toStringTag
The behavior of Object toString
can be customized using a special object property Symbol.toStringTag
.
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
Mixins can also make use of inheritance inside themselves. super
looks for parent methods in [[HomeObject]].[[Prototype]]
.
Last updated