226 lines
5.3 KiB
Markdown
226 lines
5.3 KiB
Markdown
|
|
# Static properties and methods
|
|
|
|
We can also assign a method to the class function, not to its `"prototype"`. Such methods are called *static*.
|
|
|
|
An example:
|
|
|
|
```js run
|
|
class User {
|
|
*!*
|
|
static staticMethod() {
|
|
*/!*
|
|
alert(this === User);
|
|
}
|
|
}
|
|
|
|
User.staticMethod(); // true
|
|
```
|
|
|
|
That actually does the same as assigning it as a function property:
|
|
|
|
```js
|
|
function User() { }
|
|
|
|
User.staticMethod = function() {
|
|
alert(this === User);
|
|
};
|
|
```
|
|
|
|
The value of `this` inside `User.staticMethod()` is the class constructor `User` itself (the "object before dot" rule).
|
|
|
|
Usually, static methods are used to implement functions that belong to the class, but not to any particular object of it.
|
|
|
|
For instance, we have `Article` objects and need a function to compare them. The natural choice would be `Article.compare`, like this:
|
|
|
|
```js run
|
|
class Article {
|
|
constructor(title, date) {
|
|
this.title = title;
|
|
this.date = date;
|
|
}
|
|
|
|
*!*
|
|
static compare(articleA, articleB) {
|
|
return articleA.date - articleB.date;
|
|
}
|
|
*/!*
|
|
}
|
|
|
|
// usage
|
|
let articles = [
|
|
new Article("Mind", new Date(2019, 1, 1)),
|
|
new Article("Body", new Date(2019, 0, 1)),
|
|
new Article("JavaScript", new Date(2019, 11, 1))
|
|
];
|
|
|
|
*!*
|
|
articles.sort(Article.compare);
|
|
*/!*
|
|
|
|
alert( articles[0].title ); // Body
|
|
```
|
|
|
|
Here `Article.compare` stands "over" the articles, as a means to compare them. It's not a method of an article, but rather of the whole class.
|
|
|
|
Another example would be a so-called "factory" method. Imagine, we need few ways to create an article:
|
|
|
|
1. Create by given parameters (`title`, `date` etc).
|
|
2. Create an empty article with today's date.
|
|
3. ...
|
|
|
|
The first way can be implemented by the constructor. And for the second one we can make a static method of the class.
|
|
|
|
Like `Article.createTodays()` here:
|
|
|
|
```js run
|
|
class Article {
|
|
constructor(title, date) {
|
|
this.title = title;
|
|
this.date = date;
|
|
}
|
|
|
|
*!*
|
|
static createTodays() {
|
|
// remember, this = Article
|
|
return new this("Today's digest", new Date());
|
|
}
|
|
*/!*
|
|
}
|
|
|
|
let article = Article.createTodays();
|
|
|
|
alert( article.title ); // Todays digest
|
|
```
|
|
|
|
Now every time we need to create a today's digest, we can call `Article.createTodays()`. Once again, that's not a method of an article, but a method of the whole class.
|
|
|
|
Static methods are also used in database-related classes to search/save/remove entries from the database, like this:
|
|
|
|
```js
|
|
// assuming Article is a special class for managing articles
|
|
// static method to remove the article:
|
|
Article.remove({id: 12345});
|
|
```
|
|
|
|
## Static properties
|
|
|
|
[recent browser=Chrome]
|
|
|
|
Static properties are also possible, just like regular class properties:
|
|
|
|
```js run
|
|
class Article {
|
|
static publisher = "Ilya Kantor";
|
|
}
|
|
|
|
alert( Article.publisher ); // Ilya Kantor
|
|
```
|
|
|
|
That is the same as a direct assignment to `Article`:
|
|
|
|
```js
|
|
Article.publisher = "Ilya Kantor";
|
|
```
|
|
|
|
## Statics and inheritance
|
|
|
|
Statics are inherited, we can access `Parent.method` as `Child.method`.
|
|
|
|
For instance, `Animal.compare` in the code below is inherited and accessible as `Rabbit.compare`:
|
|
|
|
```js run
|
|
class Animal {
|
|
|
|
constructor(name, speed) {
|
|
this.speed = speed;
|
|
this.name = name;
|
|
}
|
|
|
|
run(speed = 0) {
|
|
this.speed += speed;
|
|
alert(`${this.name} runs with speed ${this.speed}.`);
|
|
}
|
|
|
|
*!*
|
|
static compare(animalA, animalB) {
|
|
return animalA.speed - animalB.speed;
|
|
}
|
|
*/!*
|
|
|
|
}
|
|
|
|
// Inherit from Animal
|
|
class Rabbit extends Animal {
|
|
hide() {
|
|
alert(`${this.name} hides!`);
|
|
}
|
|
}
|
|
|
|
let rabbits = [
|
|
new Rabbit("White Rabbit", 10),
|
|
new Rabbit("Black Rabbit", 5)
|
|
];
|
|
|
|
*!*
|
|
rabbits.sort(Rabbit.compare);
|
|
*/!*
|
|
|
|
rabbits[0].run(); // Black Rabbit runs with speed 5.
|
|
```
|
|
|
|
Now we can call `Rabbit.compare` assuming that the inherited `Animal.compare` will be called.
|
|
|
|
How does it work? Again, using prototypes. As you might have already guessed, extends also gives `Rabbit` the `[[Prototype]]` reference to `Animal`.
|
|
|
|
|
|

|
|
|
|
So, `Rabbit` function now inherits from `Animal` function. And `Animal` function normally has `[[Prototype]]` referencing `Function.prototype`, because it doesn't `extend` anything.
|
|
|
|
Here, let's check that:
|
|
|
|
```js run
|
|
class Animal {}
|
|
class Rabbit extends Animal {}
|
|
|
|
// for static properties and methods
|
|
alert(Rabbit.__proto__ === Animal); // true
|
|
|
|
// and the next step is Function.prototype
|
|
alert(Animal.__proto__ === Function.prototype); // true
|
|
|
|
// that's in addition to the "normal" prototype chain for object methods
|
|
alert(Rabbit.prototype.__proto__ === Animal.prototype);
|
|
```
|
|
|
|
This way `Rabbit` has access to all static methods of `Animal`.
|
|
|
|
## Summary
|
|
|
|
Static methods are used for the functionality that doesn't relate to a concrete class instance, doesn't require an instance to exist, but rather belongs to the class as a whole, like `Article.compare` -- a generic method to compare two articles.
|
|
|
|
Static properties are used when we'd like to store class-level data, also not bound to an instance.
|
|
|
|
The syntax is:
|
|
|
|
```js
|
|
class MyClass {
|
|
static property = ...;
|
|
|
|
static method() {
|
|
...
|
|
}
|
|
}
|
|
```
|
|
|
|
That's technically the same as assigning to the class itself:
|
|
|
|
```js
|
|
MyClass.property = ...
|
|
MyClass.method = ...
|
|
```
|
|
|
|
Static properties are inherited.
|
|
|
|
Technically, for `class B extends A` the prototype of the class `B` itself points to `A`: `B.[[Prototype]] = A`. So if a field is not found in `B`, the search continues in `A`.
|