Skip to content

Commit 40ca6da

Browse files
committed
up
1 parent 2a0516d commit 40ca6da

File tree

6 files changed

+709
-55
lines changed

6 files changed

+709
-55
lines changed

1-js/07-object-oriented-programming/09-class/article.md

Lines changed: 83 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,16 @@ let user = new User("John");
4343
user.sayHi();
4444
```
4545

46-
It's easy to see that the two examples are alike. So, what exactly does `class` do? We may think that it defines a new language-level entity, but that would be wrong.
46+
It's easy to see that the two examples are alike. Just please note that methods in a class do not have a comma between them. Notice developers sometimes forget it and put a comma between class methods, and things don't work. That's not a literal object, but a class syntax.
47+
48+
So, what exactly does `class` do? We may think that it defines a new language-level entity, but that would be wrong.
4749

4850
The `class User {...}` here actually does two things:
4951

5052
1. Declares a variable `User` that references the function named `"constructor"`.
5153
2. Puts into `User.prototype` methods listed in the definition. Here it includes `sayHi` and the `constructor`.
5254

53-
Here's some code to demonstrate that:
55+
Here's the code to dig into the class and see that:
5456

5557
```js run
5658
class User {
@@ -69,15 +71,19 @@ alert(User == User.prototype.constructor); // true
6971
alert(Object.getOwnPropertyNames(User.prototype)); // constructor, sayHi
7072
```
7173

72-
Here's the illustration of `class User`:
74+
Here's the illustration of what `class User` creates:
7375

7476
![](class-user.png)
7577

76-
So `class` is a special syntax to define the constructor with prototype methods.
7778

78-
...But not only that. There are minor tweaks here and there to ensure the right usage.
7979

80-
For instance, the `constructor` function can't be called without `new`:
80+
So `class` is a special syntax to define a constructor together with its prototype methods.
81+
82+
...But not only that. There are minor tweaks here and there:
83+
84+
Constructors require `new`
85+
: Unlike a regular function, a class `constructor` can't be called without `new`:
86+
8187
```js run
8288
class User {
8389
constructor() {}
@@ -87,23 +93,19 @@ alert(typeof User); // function
8793
User(); // Error: Class constructor User cannot be invoked without 'new'
8894
```
8995

90-
```smart header="Outputting a class"
91-
If we output it like `alert(User)`, some engines show `"class User..."`, while others show `"function User..."`.
96+
Different string output
97+
: If we output it like `alert(User)`, some engines show `"class User..."`, while others show `"function User..."`.
9298

9399
Please don't be confused: the string representation may vary, but that's still a function, there is no separate "class" entity in JavaScript language.
94-
```
95100

96-
```smart header="Class methods are non-enumerable"
97-
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.
98-
```
101+
Class methods are non-enumerable
102+
: 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.
99103

100-
```smart header="What if there's no constructor?"
101-
If there's no `constructor` in the `class` construct, then an empty function is generated, same as if we had written `constructor() {}`.
102-
```
104+
Classes have a default `constructor() {}`
105+
: If there's no `constructor` in the `class` construct, then an empty function is generated, same as if we had written `constructor() {}`.
103106

104-
```smart header="Classes always `use strict`"
105-
All code inside the class construct is automatically in strict mode.
106-
```
107+
Classes always `use strict`
108+
: All code inside the class construct is automatically in strict mode.
107109

108110
### Getters/setters
109111

@@ -141,13 +143,26 @@ alert(user.name); // John
141143
user = new User(""); // Name too short.
142144
```
143145

144-
### Only methods
146+
Internally, getters and setters are also created on the `User` prototype, like this:
145147

146-
Unlike object literals, no `property:value` assignments are allowed inside `class`. There may be only methods (without a comma between them) and getters/setters.
148+
```js
149+
Object.defineProperty(User.prototype, {
150+
name: {
151+
get() {
152+
return this._name
153+
},
154+
set(name) {
155+
// ...
156+
}
157+
}
158+
});
159+
```
147160

148-
The idea is that everything inside `class` goes to the prototype. And the prototype should store methods only, which are shared between objects. The data describing a concrete object state should reside in individual objects.
161+
### Only methods
149162

150-
If we really insist on putting a non-function value into the prototype, then `class` can't help here. We can alter `prototype` manually though, like this:
163+
Unlike object literals, no `property:value` assignments are allowed inside `class`. There may be only methods and getters/setters. There is some work going on in the specification to lift that limitation, but it's not yet there.
164+
165+
If we really need to put a non-function value into the prototype, then we can alter `prototype` manually, like this:
151166

152167
```js run
153168
class User { }
@@ -157,9 +172,9 @@ User.prototype.test = 5;
157172
alert( new User().test ); // 5
158173
```
159174

160-
So, technically that's possible, but we should know why we're doing it.
175+
So, technically that's possible, but we should know why we're doing it. Such properties will be shared among all objects of the class.
161176

162-
An alternative here would be to use a getter:
177+
An "in-class" alternative is to use a getter:
163178

164179
```js run
165180
class User {
@@ -171,7 +186,7 @@ class User {
171186
alert( new User().test ); // 5
172187
```
173188

174-
From the external code, the usage is the same. But the getter variant is probably a bit slower.
189+
From the external code, the usage is the same. But the getter variant is a bit slower.
175190

176191
## Class Expression
177192

@@ -180,8 +195,9 @@ Just like functions, classes can be defined inside another expression, passed ar
180195
Here's a class-returning function ("class factory"):
181196

182197
```js run
183-
function getClass(phrase) {
198+
function makeClass(phrase) {
184199
*!*
200+
// declare a class and return it
185201
return class {
186202
sayHi() {
187203
alert(phrase);
@@ -190,30 +206,31 @@ function getClass(phrase) {
190206
*/!*
191207
}
192208

193-
let User = getClass("Hello");
209+
let User = makeClass("Hello");
194210

195211
new User().sayHi(); // Hello
196212
```
197213

198-
That's quite normal if we recall that `class` is just a special form of function-with-prototype definition.
214+
That's quite normal if we recall that `class` is just a special form of a function-with-prototype definition.
199215

200216
And, like Named Function Expressions, such classes also may have a name, that is visible inside that class only:
201217

202218
```js run
219+
// "Named Class Expression" (alas, no such term, but that's what's going on)
203220
let User = class *!*MyClass*/!* {
204221
sayHi() {
205-
alert(MyClass);
222+
alert(MyClass); // MyClass is visible only inside the class
206223
}
207224
};
208225

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

211-
alert(MyClass); // error, MyClass is only visible in methods of the class
228+
alert(MyClass); // error, MyClass not visible outside of the class
212229
```
213230

214231
## Static methods
215232

216-
Static methods are bound to the class function, not to its `"prototype"`.
233+
We can also assign methods to the class function, not to its `"prototype"`. Such methods are called *static*.
217234

218235
An example:
219236

@@ -241,7 +258,7 @@ User.staticMethod = function() {
241258

242259
The value of `this` inside `User.staticMethod()` is the class constructor `User` itself (the "object before dot" rule).
243260

244-
Usually, static methods are used when the code is related to the class, but not to a particular object of it.
261+
Usually, static methods are used to implement functions that belong to the class, but not to any particular object of it.
245262

246263
For instance, we have `Article` objects and need a function to compare them. The natural choice would be `Article.compare`, like this:
247264

@@ -273,9 +290,15 @@ articles.sort(Article.compare);
273290
alert( articles[0].title ); // Body
274291
```
275292

276-
Here `Article.compare` stands "over" the articles, as a means to compare them.
293+
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.
294+
295+
Another example would be a so-called "factory" method. Imagine, we need few ways to create an article:
277296

278-
Another example would be a so-called "factory" method, that creates an object with specific parameters.
297+
1. Create by given parameters (`title`, `date` etc).
298+
2. Create an empty article with today's date.
299+
3. ...
300+
301+
The first way can be implemented by the constructor. And for the second one we can make a static method of the class.
279302

280303
Like `Article.createTodays()` here:
281304

@@ -299,12 +322,36 @@ let article = Article.createTodays();
299322
alert( articles.title ); // Todays digest
300323
```
301324

302-
Now every time we need to create a todays digest, we can call `Article.createTodays()`.
325+
Now every time we need to create a todays digest, we can call `Article.createTodays()`. Once again, that's not a method of an article, but a method of the whole class.
303326

304-
Static methods are often used in database-related classes to search/save/remove entries from the database, like this:
327+
Static methods are also used in database-related classes to search/save/remove entries from the database, like this:
305328

306329
```js
307330
// assuming Article is a special class for managing articles
308331
// static method to remove the article:
309332
Article.remove({id: 12345});
310333
```
334+
335+
## Summary
336+
337+
The basic class syntax looks like this:
338+
339+
```js
340+
class MyClass {
341+
constructor(...) {
342+
// ...
343+
}
344+
method1(...) {}
345+
method2(...) {}
346+
get something(...) {}
347+
set something(...) {}
348+
static staticMethod(..) {}
349+
// ...
350+
}
351+
```
352+
353+
The value of `MyClass` is a function provided as `constructor`. If there's no `constructor`, then an empty function.
354+
355+
In any case, methods listed in the class declaration become members of its `prototype`, with the exception of static methods that are written into the function itself and callable as `MyClass.staticMethod()`. Static methods are used when we need a function bound to a class, but not to any object of that class.
356+
357+
In the next chapter we'll learn more about classes, including inheritance.

1-js/07-object-oriented-programming/13-mixins/article.md

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
# Mixins
22

3-
In JavaScript we can only inherit from a single object. There can be only one `[[Prototype]]`.
3+
In JavaScript we can only inherit from a single object. There can be only one `[[Prototype]]` for an object. And a class may extend only one other class.
44

5-
But sometimes we need such kind of thing. For instance, we have a code that implements events exchange or templating, and we'd like to be able to add these capabilities to any class easily.
5+
But sometimes that feels limiting. For instance, I have a class `StreetSweeper` and a class `Bycicle`, and want to make a `StreetSweepingBycicle`.
66

7-
What can help here is *mixins*.
7+
Or, talking about programming, we have a class `Renderer` that implements templating and a class `EventEmitter` that implements event handling, and want to merge these functionalities together with a class `Page`, to make a page that can use templates and emit events.
88

9-
As [defined in Wikipedia](https://en.wikipedia.org/wiki/Mixin), a *mixin* is a class that contains methods for use by other classes without having to be the parent class of those other classes.
9+
There's a concept that can help here, called "mixins".
1010

11-
In other words, a *mixin* is a class that implements a certain behavior. But we do not use it alone, we use it to add the behavior to other classes.
11+
As defined in Wikipedia, a [mixin](https://en.wikipedia.org/wiki/Mixin) is a class that contains methods for use by other classes without having to be the parent class of those other classes.
12+
13+
In other words, a *mixin* provides methods that implement a certain behavior, but we do not use it alone, we use it to add the behavior to other classes.
1214

1315
## A mixin example
1416

15-
The simplest way to make a mixin in JavaScript -- is to make an object with useful methods, that we can just copy into the prototype.
17+
The simplest way to make a mixin in JavaScript is to make an object with useful methods, so that we can easily merge them into a prototype of any class.
1618

1719
For instance here the mixin `sayHiMixin` is used to add some "speech" for `User`:
1820

@@ -45,9 +47,17 @@ Object.assign(User.prototype, sayHiMixin);
4547
new User("Dude").sayHi(); // Hi Dude!
4648
```
4749

48-
There's no inheritance, there's a simple method copying. So `User` may extend some other class and also include the mixin to "mix-in" the additional methods.
50+
There's no inheritance, but a simple method copying. So `User` may extend some other class and also include the mixin to "mix-in" the additional methods, like this:
51+
52+
```js
53+
class User extends Person {
54+
// ...
55+
}
56+
57+
Object.assign(User.prototype, sayHiMixin);
58+
```
4959

50-
Mixins also can make use of inheritance.
60+
Mixins can make use of inheritance inside themselves.
5161

5262
For instance, here `sayHiMixin` inherits from `sayMixin`:
5363

@@ -59,8 +69,7 @@ let sayMixin = {
5969
};
6070

6171
let sayHiMixin = {
62-
// can use any way of prototype setting here
63-
__proto__: sayMixin,
72+
__proto__: sayMixin, // (or we could use Object.create to set the prototype here)
6473

6574
sayHi() {
6675
*!*
@@ -94,25 +103,22 @@ That's because methods from `sayHiMixin` have `[[HomeObject]]` set to it. So `su
94103

95104
## EventMixin
96105

97-
Now a mixin for the real life.
106+
Now let's make a mixin for real life.
98107

99108
The important feature of many objects is working with events.
100109

101-
That is: an object should have a method to "generate an event" when something important happens to it, and other objects should be able to "subscribe" to receive such notifications.
110+
That is: an object should have a method to "generate an event" when something important happens to it, and other objects should be able to "listen" to such events.
102111

103-
An event must have a name and, if necessary, the attached data.
112+
An event must have a name and, optionally, bundle some additional data.
104113

105-
For instance, an object `user` can generate an event `"login"` when the visitor logs in. And an object `calendar` may want to receive such notifications and load the information about that visitor.
114+
For instance, an object `user` can generate an event `"login"` when the visitor logs in. And another object `calendar` may want to receive such events to load the calendar for the logged-in person.
106115

107-
Or, the object `menu` can generate the event `"select"` when a menu item is selected, and other objects may want to get that information and react on that event.
116+
Or, a `menu` can generate the event `"select"` when a menu item is selected, and other objects may want to get that information and react on that event.
108117

109-
Events is a way to "share information" with anyone who wants it.
110-
111-
Here is `eventMixin` that implements the corresponding methods:
118+
Events is a way to "share information" with anyone who wants it. They can be useful in any class, so let's make a mixin for them:
112119

113120
```js run
114121
let eventMixin = {
115-
116122
/**
117123
* Subscribe to event, usage:
118124
* menu.on('select', function(item) { ... }

0 commit comments

Comments
 (0)