◐ Shell
reader mode source ↗
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
File filter
Conversations
Jump to
Diff view
Apply and reload
Show whitespace
Diff view
Apply and reload
7 changes: 4 additions & 3 deletions 1-js/09-classes/01-class/1-rewrite-to-class/task.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
importance: 5

---

# Rewrite to class

The `Clock` class is written in functional style. Rewrite it the "class" syntax.

P.S. The clock ticks in the console, open it to see.
183 changes: 91 additions & 92 deletions 1-js/09-classes/01-class/article.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@

# Class basic syntax

```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() { ... }
Expand All @@ -25,11 +25,11 @@ class MyClass {
}
```

Then use `new MyClass()` to create a new object with all the listed methods.

The `constructor()` method is called automatically by `new`, so we can initialize the object there.

For example:

```js run
class User {
Expand All @@ -49,88 +49,87 @@ 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.

...Then we can call object methods, such as `user.sayHi()`.


```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?

So, what exactly is a `class`? That's not an entirely new language-level entity, as one might think.

Let's unveil any magic and see what a class really is. That'll help in understanding many complex aspects.

In JavaScript, a class is a kind of a function.

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:

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).
2. Stores class methods, such as `sayHi`, in `User.prototype`.

Afterwards, for `new User` objects, when we call a method, it's taken from the prototype, just as described in the chapter <info:function-prototype>. So the object has access to class methods.

We can illustrate the result of `class User` declaration as:

![](class-user.svg)

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
```

## Not just a syntax sugar

Sometimes people say that `class` is a "syntax 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:

```js run
// rewriting class User in pure functions

// 1. Create constructor function
function User(name) {
this.name = name;
}
// any function prototype has constructor property by default,
// so we don't need to create it

// 2. Add the method to prototype
User.prototype.sayHi = function() {
alert(this.name);
};
Expand All @@ -140,24 +139,24 @@ let user = new User("John");
user.sayHi();
```

The result of this definition is about the same. So, there are indeed reasons why `class` can be considered a syntax sugar to define a constructor together with its prototype methods.

Although, there are important differences.

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.

Unlike a regular function, a class constructor must be called with `new`:

```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 {
Expand All @@ -167,21 +166,21 @@ Although, there are important differences.
alert(User); // class User { ... }
```

2. Class methods are non-enumerable.
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.

3. Classes always `use strict`.
All code inside the class construct is automatically in strict mode.

Besides, `class` syntax brings many other features that we'll explore later.

## Class Expression

Just like functions, classes can be defined inside another expression, passed around, returned, assigned etc.

Here's an example of a class expression:

```js
let User = class {
Expand All @@ -191,55 +190,55 @@ let User = class {
};
```

Similar to Named Function Expressions, class expressions may have a name.

If a class expression has a name, it's visible inside the class only:

```js run
// "Named Class Expression"
// (no such term in the spec, but that's similar to Named Function Expression)
let User = class *!*MyClass*/!* {
sayHi() {
alert(MyClass); // MyClass name is visible only inside the class
}
};

new User().sayHi(); // works, shows MyClass definition

alert(MyClass); // error, MyClass name isn't visible outside of the class
```


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
```


## Getters/setters, other shorthands

Just like literal objects, classes may include getters/setters, generators, computed properties etc.

Here's an example for `user.name` implemented using `get/set`:

```js run
class User {

constructor(name) {
// invokes the setter
this.name = name;
}

Expand All @@ -264,10 +263,10 @@ class User {
let user = new User("John");
alert(user.name); // John

user = new User(""); // Name too short.
```

The class declaration creates getters and setters in `User.prototype`, like this:

```js
Object.defineProperties(User.prototype, {
Expand All @@ -282,7 +281,7 @@ Object.defineProperties(User.prototype, {
});
```

Here's an example with a computed property in brackets `[...]`:

```js run
class User {
Expand All @@ -298,15 +297,15 @@ class User {
new User().sayHi();
```

For a generator method, similarly, prepend it with `*`.

## Class properties

```warn header="Old browsers may need a polyfill"
Class-level properties are a recent addition to the language.
```

In the example above, `User` only had methods. Let's add a property:

```js run
class User {
@@ -322,30 +321,30 @@ class User {
new User().sayHi();
```

The property `name` is not placed into `User.prototype`. Instead, it is created by `new` before calling constructor, it's the property of the object itself.

## Summary

The basic class syntax looks like this:

```js
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)
// ...
}
```

`MyClass` is technically a function (the one that we provide as `constructor`), while methods, getters and settors are written to `MyClass.prototype`.

In the next chapters we'll learn more about classes, including inheritance and other features.
Loading
Toggle all file notes Toggle all file annotations