2019-04-21 13:40:15 +03:00
2019-04-21 22:30:06 +03:00
# Class basic syntax
2019-04-21 13:40:15 +03:00
```quote author="Wikipedia"
In object-oriented programming, a *class* is an extensible program-code-template for creating objects, providing initial values for state (member variables) and implementations of behavior (member functions or methods).
` ``
In practice, we often need to create many objects of the same kind, like users, or goods or whatever.
As we already know from the chapter <info:constructor-new>, ` new function` can help with that.
But in the modern JavaScript, there's a more advanced "class" construct, that introduces great new features which are useful for object-oriented programming.
## The "class" syntax
The basic syntax is:
` ``js
class MyClass {
// class methods
constructor() { ... }
method1() { ... }
method2() { ... }
method3() { ... }
...
}
` ``
2019-08-04 11:29:15 +03:00
Then use ` new MyClass()` to create a new object with all the listed methods.
2019-04-21 13:40:15 +03:00
The ` constructor()` method is called automatically by ` new`, so we can initialize the object there.
For example:
` ``js run
class User {
constructor(name) {
this.name = name;
}
sayHi() {
alert(this.name);
}
}
// Usage:
let user = new User("John");
user.sayHi();
` ``
When ` new User("John")` is called:
1. A new object is created.
2. The ` constructor` runs with the given argument and assigns ` this.name` to it.
2019-08-04 11:29:15 +03:00
...Then we can call object methods, such as ` user.sayHi()`.
2019-04-21 13:40:15 +03:00
` ``warn header="No comma between class methods"
A common pitfall for novice developers is to put a comma between class methods, which would result in a syntax error.
The notation here is not to be confused with object literals. Within the class, no commas are required.
` ``
## What is a class?
2019-06-01 21:00:34 +03:00
So, what exactly is a ` class`? That's not an entirely new language-level entity, as one might think.
2019-04-21 13:40:15 +03:00
Let's unveil any magic and see what a class really is. That'll help in understanding many complex aspects.
2019-10-22 14:37:34 +02:00
In JavaScript, a class is a kind of function.
2019-04-21 13:40:15 +03:00
Here, take a look:
` ``js run
class User {
constructor(name) { this.name = name; }
sayHi() { alert(this.name); }
}
// proof: User is a function
*!*
alert(typeof User); // function
*/!*
` ``
What ` class User {...}` construct really does is:
2019-07-17 09:57:01 +02:00
1. Creates a function named ` User`, that becomes the result of the class declaration. The function code is taken from the ` constructor` method (assumed empty if we don't write such method).
2019-07-18 19:49:48 +03:00
2. Stores class methods, such as ` sayHi`, in ` User.prototype`.
2019-04-21 13:40:15 +03:00
2019-10-02 10:36:06 +03:00
After ` new User` object is created, when we call its method, it's taken from the prototype, just as described in the chapter <info:function-prototype>. So the object has access to class methods.
2019-04-21 13:40:15 +03:00
2019-06-01 21:00:34 +03:00
We can illustrate the result of ` class User` declaration as:
2019-04-21 13:40:15 +03:00
2019-07-28 15:42:37 +03:00

2019-04-21 13:40:15 +03:00
Here's the code to introspect it:
` ``js run
class User {
constructor(name) { this.name = name; }
sayHi() { alert(this.name); }
}
// class is a function
alert(typeof User); // function
// ...or, more precisely, the constructor method
alert(User === User.prototype.constructor); // true
// The methods are in User.prototype, e.g:
alert(User.prototype.sayHi); // alert(this.name);
// there are exactly two methods in the prototype
alert(Object.getOwnPropertyNames(User.prototype)); // constructor, sayHi
` ``
2019-11-05 13:50:42 +03:00
## Not just a syntactic sugar
2019-04-21 13:40:15 +03:00
2019-11-05 13:50:42 +03:00
Sometimes people say that ` class` is a "syntactic sugar" (syntax that is designed to make things easier to read, but doesn't introduce anything new), because we could actually declare the same without ` class` keyword at all:
2019-04-21 13:40:15 +03:00
` ``js run
// rewriting class User in pure functions
// 1. Create constructor function
function User(name) {
this.name = name;
}
2020-05-03 00:59:05 +03:00
// a function prototype has "constructor" property by default,
2019-04-21 13:40:15 +03:00
// so we don't need to create it
// 2. Add the method to prototype
User.prototype.sayHi = function() {
alert(this.name);
};
// Usage:
let user = new User("John");
user.sayHi();
` ``
2019-11-05 13:50:42 +03:00
The result of this definition is about the same. So, there are indeed reasons why ` class` can be considered a syntactic sugar to define a constructor together with its prototype methods.
2019-04-21 13:40:15 +03:00
2019-10-01 21:46:38 -06:00
Still, there are important differences.
2019-04-21 13:40:15 +03:00
1. First, a function created by ` class` is labelled by a special internal property ` [[FunctionKind]]:"classConstructor"`. So it's not entirely the same as creating it manually.
2020-05-03 00:59:05 +03:00
The language checks for that property in a variety of places. For example, unlike a regular function, it must be called with ` new`:
2019-04-21 13:40:15 +03:00
` ``js run
class User {
constructor() {}
}
alert(typeof User); // function
User(); // Error: Class constructor User cannot be invoked without 'new'
` ``
Also, a string representation of a class constructor in most JavaScript engines starts with the "class..."
` ``js run
class User {
constructor() {}
}
alert(User); // class User { ... }
` ``
2020-05-03 00:59:05 +03:00
There are other differences, we'll see them soon.
2019-04-21 13:40:15 +03:00
2019-05-08 09:31:05 +02:00
2. Class methods are non-enumerable.
2019-04-21 13:40:15 +03:00
A class definition sets ` enumerable` flag to ` false` for all methods in the ` "prototype"`.
That's good, because if we ` for..in` over an object, we usually don't want its class methods.
2019-05-08 09:31:05 +02:00
3. Classes always ` use strict`.
2019-04-21 13:40:15 +03:00
All code inside the class construct is automatically in strict mode.
2019-07-18 19:41:21 +03:00
Besides, ` class` syntax brings many other features that we'll explore later.
2019-04-21 13:40:15 +03:00
## Class Expression
2019-10-22 14:37:34 +02:00
Just like functions, classes can be defined inside another expression, passed around, returned, assigned, etc.
2019-04-21 13:40:15 +03:00
Here's an example of a class expression:
` ``js
let User = class {
sayHi() {
alert("Hello");
}
};
` ``
2019-08-04 11:29:15 +03:00
Similar to Named Function Expressions, class expressions may have a name.
2019-04-21 13:40:15 +03:00
If a class expression has a name, it's visible inside the class only:
` ``js run
2019-06-01 21:00:34 +03:00
// "Named Class Expression"
// (no such term in the spec, but that's similar to Named Function Expression)
2019-04-21 13:40:15 +03:00
let User = class *!*MyClass*/!* {
sayHi() {
2019-08-04 11:29:15 +03:00
alert(MyClass); // MyClass name is visible only inside the class
2019-04-21 13:40:15 +03:00
}
};
new User().sayHi(); // works, shows MyClass definition
2019-08-04 11:29:15 +03:00
alert(MyClass); // error, MyClass name isn't visible outside of the class
2019-04-21 13:40:15 +03:00
` ``
We can even make classes dynamically "on-demand", like this:
` ``js run
function makeClass(phrase) {
// declare a class and return it
return class {
sayHi() {
alert(phrase);
};
};
}
// Create a new class
let User = makeClass("Hello");
new User().sayHi(); // Hello
` ``
2020-05-03 00:59:05 +03:00
## Getters/setters
2019-04-21 13:40:15 +03:00
2019-09-21 22:16:45 +03:00
Just like literal objects, classes may include getters/setters, computed properties etc.
2019-04-21 13:40:15 +03:00
Here's an example for ` user.name` implemented using ` get/set`:
` ``js run
class User {
constructor(name) {
// invokes the setter
2019-05-15 21:55:01 +10:00
this.name = name;
2019-04-21 13:40:15 +03:00
}
*!*
get name() {
*/!*
return this._name;
}
*!*
set name(value) {
*/!*
if (value.length < 4) {
alert("Name is too short.");
return;
}
this._name = value;
}
}
let user = new User("John");
alert(user.name); // John
2020-01-20 15:34:31 +08:00
user = new User(""); // Name is too short.
2019-04-21 13:40:15 +03:00
` ``
2020-05-03 00:59:05 +03:00
Technically, such class declaration works by creating getters and setters in ` User.prototype`.
2019-04-21 13:40:15 +03:00
2020-05-03 00:59:05 +03:00
## Computed names [...]
2019-04-21 13:40:15 +03:00
2020-05-03 00:59:05 +03:00
Here's an example with a computed method name using brackets ` [...]`:
2019-04-21 13:40:15 +03:00
` ``js run
class User {
2019-08-04 11:29:15 +03:00
*!*
['say' + 'Hi']() {
*/!*
2019-04-21 13:40:15 +03:00
alert("Hello");
}
}
new User().sayHi();
` ``
2020-05-03 00:59:05 +03:00
Such features are easy to remember, as they resemble that of literal objects.
2020-04-03 02:06:31 +03:00
## Class fields
2019-04-21 13:40:15 +03:00
` ``warn header="Old browsers may need a polyfill"
2020-04-03 02:06:31 +03:00
Class fields are a recent addition to the language.
2019-04-21 13:40:15 +03:00
` ``
2020-05-03 00:59:05 +03:00
Previously, our classes only had methods.
2020-04-03 02:06:31 +03:00
2020-04-03 01:35:49 +02:00
"Class fields" is a syntax that allows to add any properties.
2020-04-03 02:06:31 +03:00
For instance, let's add ` name` property to ` class User`:
2019-04-21 13:40:15 +03:00
` ``js run
class User {
2019-08-04 11:29:15 +03:00
*!*
2020-05-03 00:59:05 +03:00
name = "John";
2019-08-04 11:29:15 +03:00
*/!*
2019-04-21 13:40:15 +03:00
sayHi() {
alert(` Hello, ${this.name}!`);
}
}
2020-05-03 00:59:05 +03:00
new User().sayHi(); // Hello, John!
` ``
So, we just write "<property name> = <value>" in the declaration, and that's it.
The important difference of class fields is that they are set on individual objects, not ` User.prototype`:
2019-11-05 14:48:31 +03:00
2020-05-03 00:59:05 +03:00
` ``js run
class User {
*!*
name = "John";
*/!*
}
let user = new User();
alert(user.name); // John
alert(User.prototype.name); // undefined
2019-04-21 13:40:15 +03:00
` ``
2020-07-01 11:54:48 +03:00
We can also assign values using more complex expressions and function calls:
2020-05-03 00:59:05 +03:00
` ``js run
class User {
*!*
name = prompt("Name, please?", "John");
*/!*
}
2020-04-03 02:06:31 +03:00
2020-05-03 00:59:05 +03:00
let user = new User();
alert(user.name); // John
` ``
2020-04-03 02:06:31 +03:00
2020-07-01 11:54:48 +03:00
2020-04-03 02:06:31 +03:00
### Making bound methods with class fields
2020-04-04 15:12:23 +03:00
As demonstrated in the chapter <info:bind> functions in JavaScript have a dynamic ` this`. It depends on the context of the call.
2020-04-04 15:08:43 +03:00
2020-04-04 15:12:23 +03:00
So if an object method is passed around and called in another context, ` this` won't be a reference to its object any more.
For instance, this code will show ` undefined`:
2020-04-03 02:06:31 +03:00
` ``js run
class Button {
constructor(value) {
this.value = value;
}
2020-04-04 15:08:43 +03:00
click() {
2020-04-03 02:06:31 +03:00
alert(this.value);
}
}
2020-04-04 15:08:43 +03:00
let button = new Button("hello");
2020-04-03 02:06:31 +03:00
2020-04-04 15:08:43 +03:00
*!*
setTimeout(button.click, 1000); // undefined
*/!*
2020-04-03 02:06:31 +03:00
` ``
2020-04-04 15:12:23 +03:00
The problem is called "losing ` this`".
2020-04-04 15:15:09 +03:00
There are two approaches to fixing it, as discussed in the chapter <info:bind>:
2020-04-03 02:06:31 +03:00
2020-04-04 15:08:43 +03:00
1. Pass a wrapper-function, such as ` setTimeout(() => button.click(), 1000)`.
2020-07-01 11:54:48 +03:00
2. Bind the method to object, e.g. in the constructor.
2020-04-04 15:08:43 +03:00
2020-07-01 11:54:48 +03:00
Class fields provide another, quite elegant syntax:
2020-04-04 15:08:43 +03:00
` ``js run
class Button {
constructor(value) {
this.value = value;
}
*!*
click = () => {
alert(this.value);
}
*/!*
2020-04-03 02:06:31 +03:00
}
2020-04-04 15:08:43 +03:00
let button = new Button("hello");
setTimeout(button.click, 1000); // hello
2020-04-03 02:06:31 +03:00
` ``
2019-04-21 13:40:15 +03:00
2020-07-01 11:54:48 +03:00
The class field ` click = () => {...}` is created on a per-object basis, there's a separate function for each ` Button` object, with ` this` inside it referencing that object. We can pass ` button.click` around anywhere, and the value of ` this` will always be correct.
2020-04-04 15:08:43 +03:00
2020-07-01 11:54:48 +03:00
That's especially useful in browser environment, for event listeners.
2020-04-04 15:08:43 +03:00
2019-04-21 13:40:15 +03:00
## Summary
The basic class syntax looks like this:
` ``js
class MyClass {
2019-08-04 11:29:15 +03:00
prop = value; // property
2019-04-21 13:40:15 +03:00
constructor(...) { // constructor
// ...
}
method(...) {} // method
get something(...) {} // getter method
set something(...) {} // setter method
2019-08-04 11:29:15 +03:00
[Symbol.iterator]() {} // method with computed name (symbol here)
2019-04-21 13:40:15 +03:00
// ...
}
` ``
2019-10-22 14:37:34 +02:00
` MyClass` is technically a function (the one that we provide as ` constructor`), while methods, getters and setters are written to ` MyClass.prototype`.
2019-04-21 13:40:15 +03:00
In the next chapters we'll learn more about classes, including inheritance and other features.