diff --git a/1-js/07-object-oriented-programming/04-function-prototype/function-prototype-constructor.png b/1-js/07-object-oriented-programming/04-function-prototype/function-prototype-constructor.png deleted file mode 100644 index cd2cdea7..00000000 Binary files a/1-js/07-object-oriented-programming/04-function-prototype/function-prototype-constructor.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/04-function-prototype/function-prototype-constructor@2x.png b/1-js/07-object-oriented-programming/04-function-prototype/function-prototype-constructor@2x.png deleted file mode 100644 index 48a5de34..00000000 Binary files a/1-js/07-object-oriented-programming/04-function-prototype/function-prototype-constructor@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/04-function-prototype/proto-constructor-animal-rabbit.png b/1-js/07-object-oriented-programming/04-function-prototype/proto-constructor-animal-rabbit.png deleted file mode 100644 index aaa6d234..00000000 Binary files a/1-js/07-object-oriented-programming/04-function-prototype/proto-constructor-animal-rabbit.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/04-function-prototype/proto-constructor-animal-rabbit@2x.png b/1-js/07-object-oriented-programming/04-function-prototype/proto-constructor-animal-rabbit@2x.png deleted file mode 100644 index 537d6ef7..00000000 Binary files a/1-js/07-object-oriented-programming/04-function-prototype/proto-constructor-animal-rabbit@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/04-function-prototype/rabbit-prototype-constructor.png b/1-js/07-object-oriented-programming/04-function-prototype/rabbit-prototype-constructor.png deleted file mode 100644 index 5d29e43d..00000000 Binary files a/1-js/07-object-oriented-programming/04-function-prototype/rabbit-prototype-constructor.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/04-function-prototype/rabbit-prototype-constructor@2x.png b/1-js/07-object-oriented-programming/04-function-prototype/rabbit-prototype-constructor@2x.png deleted file mode 100644 index ca5c3855..00000000 Binary files a/1-js/07-object-oriented-programming/04-function-prototype/rabbit-prototype-constructor@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/function-prototype-constructor.png b/1-js/07-object-oriented-programming/05-native-prototypes/function-prototype-constructor.png deleted file mode 100644 index cd2cdea7..00000000 Binary files a/1-js/07-object-oriented-programming/05-native-prototypes/function-prototype-constructor.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/function-prototype-constructor@2x.png b/1-js/07-object-oriented-programming/05-native-prototypes/function-prototype-constructor@2x.png deleted file mode 100644 index 48a5de34..00000000 Binary files a/1-js/07-object-oriented-programming/05-native-prototypes/function-prototype-constructor@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/proto-constructor-animal-rabbit.png b/1-js/07-object-oriented-programming/05-native-prototypes/proto-constructor-animal-rabbit.png deleted file mode 100644 index aaa6d234..00000000 Binary files a/1-js/07-object-oriented-programming/05-native-prototypes/proto-constructor-animal-rabbit.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/proto-constructor-animal-rabbit@2x.png b/1-js/07-object-oriented-programming/05-native-prototypes/proto-constructor-animal-rabbit@2x.png deleted file mode 100644 index 537d6ef7..00000000 Binary files a/1-js/07-object-oriented-programming/05-native-prototypes/proto-constructor-animal-rabbit@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/rabbit-prototype-constructor.png b/1-js/07-object-oriented-programming/05-native-prototypes/rabbit-prototype-constructor.png deleted file mode 100644 index 5d29e43d..00000000 Binary files a/1-js/07-object-oriented-programming/05-native-prototypes/rabbit-prototype-constructor.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/rabbit-prototype-constructor@2x.png b/1-js/07-object-oriented-programming/05-native-prototypes/rabbit-prototype-constructor@2x.png deleted file mode 100644 index ca5c3855..00000000 Binary files a/1-js/07-object-oriented-programming/05-native-prototypes/rabbit-prototype-constructor@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/09-class/article.md b/1-js/07-object-oriented-programming/09-class/article.md deleted file mode 100644 index 71878ae3..00000000 --- a/1-js/07-object-oriented-programming/09-class/article.md +++ /dev/null @@ -1,355 +0,0 @@ - -# Classes - -The "class" construct allows to define prototype-based classes with a clean, nice-looking syntax. - -## The "class" syntax - -The `class` syntax is versatile, we'll start with a simple example first. - -Here's a prototype-based class `User`: - -```js run -function User(name) { - this.name = name; -} - -User.prototype.sayHi = function() { - alert(this.name); -} - -let user = new User("John"); -user.sayHi(); -``` - -...And that's the same using `class` syntax: - -```js run -class User { - - constructor(name) { - this.name = name; - } - - sayHi() { - alert(this.name); - } - -} - -let user = new User("John"); -user.sayHi(); -``` - -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. Novice 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. - -So, what exactly does `class` do? We may think that it defines a new language-level entity, but that would be wrong. - -The `class User {...}` here actually does two things: - -1. Declares a variable `User` that references the function named `"constructor"`. -2. Puts methods listed in the definition into `User.prototype`. Here, it includes `sayHi` and the `constructor`. - -Here's the code to dig into the class and see that: - -```js run -class User { - constructor(name) { this.name = name; } - sayHi() { alert(this.name); } -} - -*!* -// proof: User is the "constructor" function -*/!* -alert(User === User.prototype.constructor); // true - -*!* -// proof: there are two methods in its "prototype" -*/!* -alert(Object.getOwnPropertyNames(User.prototype)); // constructor, sayHi -``` - -Here's the illustration of what `class User` creates: - -![](class-user.png) - - - -So `class` is a special syntax to define a constructor together with its prototype methods. - -...But not only that. There are minor tweaks here and there: - -Constructors require `new` -: Unlike a regular function, a class `constructor` can't be called without `new`: - -```js run -class User { - constructor() {} -} - -alert(typeof User); // function -User(); // Error: Class constructor User cannot be invoked without 'new' -``` - -Different string output -: If we output it like `alert(User)`, some engines show `"class User..."`, while others show `"function User..."`. - -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. - -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. - -Classes have a default `constructor() {}` -: If there's no `constructor` in the `class` construct, then an empty function is generated, same as if we had written `constructor() {}`. - -Classes always `use strict` -: All code inside the class construct is automatically in strict mode. - -### Getters/setters - -Classes may also include getters/setters. Here's an example with `user.name` implemented using them: - -```js run -class User { - - constructor(name) { - // invokes the setter - this.name = name; - } - -*!* - 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 - -user = new User(""); // Name too short. -``` - -Internally, getters and setters are also created on the `User` prototype, like this: - -```js -Object.defineProperties(User.prototype, { - name: { - get() { - return this._name - }, - set(name) { - // ... - } - } -}); -``` - -### Only methods - -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. - -If we really need to put a non-function value into the prototype, then we can alter `prototype` manually, like this: - -```js run -class User { } - -User.prototype.test = 5; - -alert( new User().test ); // 5 -``` - -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. - -An "in-class" alternative is to use a getter: - -```js run -class User { - get test() { - return 5; - } -} - -alert( new User().test ); // 5 -``` - -From the external code, the usage is the same. But the getter variant is a bit slower. - -## Class Expression - -Just like functions, classes can be defined inside another expression, passed around, returned etc. - -Here's a class-returning function ("class factory"): - -```js run -function makeClass(phrase) { -*!* - // declare a class and return it - return class { - sayHi() { - alert(phrase); - }; - }; -*/!* -} - -let User = makeClass("Hello"); - -new User().sayHi(); // Hello -``` - -That's quite normal if we recall that `class` is just a special form of a function-with-prototype definition. - -And, like Named Function Expressions, such classes also may have a name, that is visible inside that class only: - -```js run -// "Named Class Expression" (alas, no such term, but that's what's going on) -let User = class *!*MyClass*/!* { - sayHi() { - alert(MyClass); // MyClass is visible only inside the class - } -}; - -new User().sayHi(); // works, shows MyClass definition - -alert(MyClass); // error, MyClass not visible outside of the class -``` - -## Static methods - -We can also assign methods 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(2016, 1, 1)), - new Article("Body", new Date(2016, 0, 1)), - new Article("JavaScript", new Date(2016, 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}); -``` - -## Summary - -The basic class syntax looks like this: - -```js -class MyClass { - constructor(...) { - // ... - } - method1(...) {} - method2(...) {} - get something(...) {} - set something(...) {} - static staticMethod(..) {} - // ... -} -``` - -The value of `MyClass` is a function provided as `constructor`. If there's no `constructor`, then an empty function. - -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. - -In the next chapter we'll learn more about classes, including inheritance. diff --git a/1-js/07-object-oriented-programming/index.md b/1-js/07-object-oriented-programming/index.md deleted file mode 100644 index 7053ada8..00000000 --- a/1-js/07-object-oriented-programming/index.md +++ /dev/null @@ -1,3 +0,0 @@ -# Objects, classes, inheritance - -In this section we return to objects and learn them even more in-depth. diff --git a/1-js/07-object-oriented-programming/01-property-descriptors/article.md b/1-js/07-object-properties/01-property-descriptors/article.md similarity index 97% rename from 1-js/07-object-oriented-programming/01-property-descriptors/article.md rename to 1-js/07-object-properties/01-property-descriptors/article.md index 14d91800..c44e5650 100644 --- a/1-js/07-object-oriented-programming/01-property-descriptors/article.md +++ b/1-js/07-object-properties/01-property-descriptors/article.md @@ -3,7 +3,9 @@ As we know, objects can store properties. -Till now, a property was a simple "key-value" pair to us. But an object property is actually a more complex and tunable thing. +Till now, a property was a simple "key-value" pair to us. But an object property is actually a more flexible and powerful thing. + +In this chapter we'll study additional configuration options, and in the next we'll see how to invisibly turn them into getter/setter functions. ## Property flags diff --git a/1-js/07-object-oriented-programming/02-property-accessors/article.md b/1-js/07-object-properties/02-property-accessors/article.md similarity index 100% rename from 1-js/07-object-oriented-programming/02-property-accessors/article.md rename to 1-js/07-object-properties/02-property-accessors/article.md diff --git a/1-js/07-object-properties/index.md b/1-js/07-object-properties/index.md new file mode 100644 index 00000000..67fcccaf --- /dev/null +++ b/1-js/07-object-properties/index.md @@ -0,0 +1,3 @@ +# Object properties configuration + +In this section we return to objects and study their properties even more in-depth. diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/1-property-after-delete/solution.md b/1-js/08-prototypes/01-prototype-inheritance/1-property-after-delete/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/1-property-after-delete/solution.md rename to 1-js/08-prototypes/01-prototype-inheritance/1-property-after-delete/solution.md diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/1-property-after-delete/task.md b/1-js/08-prototypes/01-prototype-inheritance/1-property-after-delete/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/1-property-after-delete/task.md rename to 1-js/08-prototypes/01-prototype-inheritance/1-property-after-delete/task.md diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/2-search-algorithm/solution.md b/1-js/08-prototypes/01-prototype-inheritance/2-search-algorithm/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/2-search-algorithm/solution.md rename to 1-js/08-prototypes/01-prototype-inheritance/2-search-algorithm/solution.md diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/2-search-algorithm/task.md b/1-js/08-prototypes/01-prototype-inheritance/2-search-algorithm/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/2-search-algorithm/task.md rename to 1-js/08-prototypes/01-prototype-inheritance/2-search-algorithm/task.md diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/3-proto-and-this/solution.md b/1-js/08-prototypes/01-prototype-inheritance/3-proto-and-this/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/3-proto-and-this/solution.md rename to 1-js/08-prototypes/01-prototype-inheritance/3-proto-and-this/solution.md diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/3-proto-and-this/task.md b/1-js/08-prototypes/01-prototype-inheritance/3-proto-and-this/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/3-proto-and-this/task.md rename to 1-js/08-prototypes/01-prototype-inheritance/3-proto-and-this/task.md diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/4-hamster-proto/solution.md b/1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/4-hamster-proto/solution.md rename to 1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/solution.md diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/4-hamster-proto/task.md b/1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/4-hamster-proto/task.md rename to 1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/task.md diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/article.md b/1-js/08-prototypes/01-prototype-inheritance/article.md similarity index 81% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/article.md rename to 1-js/08-prototypes/01-prototype-inheritance/article.md index eac5ec81..c6177498 100644 --- a/1-js/07-object-oriented-programming/03-prototype-inheritance/article.md +++ b/1-js/08-prototypes/01-prototype-inheritance/article.md @@ -31,7 +31,13 @@ rabbit.__proto__ = animal; */!* ``` -Please note that `__proto__` is *not the same* as `[[Prototype]]`. That's a getter/setter for it. We'll talk about other ways of setting it later, but for now `__proto__` will do just fine. +```smart header="`__proto__` is a historical getter/setter for `[[Prototype]]`" +Please note that `__proto__` is *not the same* as `[[Prototype]]`. That's a getter/setter for it. + +It exists for historical reasons, in modern language it is replaced with functions `Object.getPrototypeOf/Object.setPrototypeOf` that also get/set the prototype. We'll study the reasons for that and these functions later. + +By the specification, `__proto__` must only be supported by browsers, but in fact all environments including server-side support it. For now, as `__proto__` notation is a little bit more intuitively obvious, we'll use it in the examples. +``` If we look for a property in `rabbit`, and it's missing, JavaScript automatically takes it from `animal`. @@ -106,12 +112,16 @@ let animal = { let rabbit = { jumps: true, +*!* __proto__: animal +*/!* }; let longEar = { earLength: 10, +*!* __proto__: rabbit +*/!* }; // walk is taken from the prototype chain @@ -124,15 +134,15 @@ alert(longEar.jumps); // true (from rabbit) There are actually only two limitations: 1. The references can't go in circles. JavaScript will throw an error if we try to assign `__proto__` in a circle. -2. The value of `__proto__` can be either an object or `null`. All other values (like primitives) are ignored. +2. The value of `__proto__` can be either an object or `null`, other types (like primitives) are ignored. Also it may be obvious, but still: there can be only one `[[Prototype]]`. An object may not inherit from two others. -## Read/write rules +## Writing doesn't use prototype The prototype is only used for reading properties. -For data properties (not getters/setters) write/delete operations work directly with the object. +Write/delete operations work directly with the object. In the example below, we assign its own `walk` method to `rabbit`: @@ -161,9 +171,9 @@ From now on, `rabbit.walk()` call finds the method immediately in the object and ![](proto-animal-rabbit-walk-2.png) -For getters/setters -- if we read/write a property, they are looked up in the prototype and invoked. +That's for data properties only, not for accessors. If a property is a getter/setter, then it behaves like a function: getters/setters are looked up in the prototype. -For instance, check out `admin.fullName` property in the code below: +For that reason `admin.fullName` works correctly in the code below: ```js run let user = { @@ -194,15 +204,15 @@ Here in the line `(*)` the property `admin.fullName` has a getter in the prototy ## The value of "this" -An interesting question may arise in the example above: what's the value of `this` inside `set fullName(value)`? Where the properties `this.name` and `this.surname` are written: `user` or `admin`? +An interesting question may arise in the example above: what's the value of `this` inside `set fullName(value)`? Where the properties `this.name` and `this.surname` are written: into `user` or `admin`? The answer is simple: `this` is not affected by prototypes at all. **No matter where the method is found: in an object or its prototype. In a method call, `this` is always the object before the dot.** -So, the setter actually uses `admin` as `this`, not `user`. +So, the setter call `admin.fullName=` uses `admin` as `this`, not `user`. -That is actually a super-important thing, because we may have a big object with many methods and inherit from it. Then we can run its methods on inherited objects and they will modify the state of these objects, not the big one. +That is actually a super-important thing, because we may have a big object with many methods and inherit from it. Then inherited objects can run its methods, and they will modify the state of these objects, not the big one. For instance, here `animal` represents a "method storage", and `rabbit` makes use of it. @@ -244,7 +254,7 @@ As a result, methods are shared, but the object state is not. ## Summary - In JavaScript, all objects have a hidden `[[Prototype]]` property that's either another object or `null`. -- We can use `obj.__proto__` to access it (there are other ways too, to be covered soon). +- We can use `obj.__proto__` to access it (a historical getter/setter, there are other ways, to be covered soon). - The object referenced by `[[Prototype]]` is called a "prototype". - If we want to read a property of `obj` or call a method, and it doesn't exist, then JavaScript tries to find it in the prototype. Write/delete operations work directly on the object, they don't use the prototype (unless the property is actually a setter). - If we call `obj.method()`, and the `method` is taken from the prototype, `this` still references `obj`. So methods always work with the current object even if they are inherited. diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/object-prototype-empty.png b/1-js/08-prototypes/01-prototype-inheritance/object-prototype-empty.png similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/object-prototype-empty.png rename to 1-js/08-prototypes/01-prototype-inheritance/object-prototype-empty.png diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/object-prototype-empty@2x.png b/1-js/08-prototypes/01-prototype-inheritance/object-prototype-empty@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/object-prototype-empty@2x.png rename to 1-js/08-prototypes/01-prototype-inheritance/object-prototype-empty@2x.png diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-chain.png b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-chain.png similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-chain.png rename to 1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-chain.png diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-chain@2x.png b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-chain@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-chain@2x.png rename to 1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-chain@2x.png diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-walk-2.png b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-2.png similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-walk-2.png rename to 1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-2.png diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-walk-2@2x.png b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-2@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-walk-2@2x.png rename to 1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-2@2x.png diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-walk-3.png b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-3.png similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-walk-3.png rename to 1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-3.png diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-walk-3@2x.png b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-3@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-walk-3@2x.png rename to 1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-3@2x.png diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-walk.png b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk.png similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-walk.png rename to 1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk.png diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-walk@2x.png b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-walk@2x.png rename to 1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk@2x.png diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit.png b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit.png similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit.png rename to 1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit.png diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit@2x.png b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit@2x.png rename to 1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit@2x.png diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-user-admin.png b/1-js/08-prototypes/01-prototype-inheritance/proto-user-admin.png similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/proto-user-admin.png rename to 1-js/08-prototypes/01-prototype-inheritance/proto-user-admin.png diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-user-admin@2x.png b/1-js/08-prototypes/01-prototype-inheritance/proto-user-admin@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/proto-user-admin@2x.png rename to 1-js/08-prototypes/01-prototype-inheritance/proto-user-admin@2x.png diff --git a/1-js/07-object-oriented-programming/04-function-prototype/1-changing-prototype/solution.md b/1-js/08-prototypes/02-function-prototype/1-changing-prototype/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/04-function-prototype/1-changing-prototype/solution.md rename to 1-js/08-prototypes/02-function-prototype/1-changing-prototype/solution.md diff --git a/1-js/07-object-oriented-programming/04-function-prototype/1-changing-prototype/task.md b/1-js/08-prototypes/02-function-prototype/1-changing-prototype/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/04-function-prototype/1-changing-prototype/task.md rename to 1-js/08-prototypes/02-function-prototype/1-changing-prototype/task.md diff --git a/1-js/07-object-oriented-programming/04-function-prototype/4-new-object-same-constructor/solution.md b/1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/04-function-prototype/4-new-object-same-constructor/solution.md rename to 1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/solution.md diff --git a/1-js/07-object-oriented-programming/04-function-prototype/4-new-object-same-constructor/task.md b/1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/04-function-prototype/4-new-object-same-constructor/task.md rename to 1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/task.md diff --git a/1-js/07-object-oriented-programming/04-function-prototype/article.md b/1-js/08-prototypes/02-function-prototype/article.md similarity index 78% rename from 1-js/07-object-oriented-programming/04-function-prototype/article.md rename to 1-js/08-prototypes/02-function-prototype/article.md index 58c7a0c5..6de6e03e 100644 --- a/1-js/07-object-oriented-programming/04-function-prototype/article.md +++ b/1-js/08-prototypes/02-function-prototype/article.md @@ -1,18 +1,14 @@ # F.prototype -In modern JavaScript we can set a prototype using `__proto__`, as described in the previous article. But it wasn't like that all the time. +Remember, new objects can be created with a constructor function, like `new F()`. -JavaScript has had prototypal inheritance from the beginning. It was one of the core features of the language. +If `F.prototype` is an object, then `new` operator uses it to set `[[Prototype]]` for the new object. -But in the old times, there was another (and the only) way to set it: to use a `"prototype"` property of the constructor function. And there are still many scripts that use it. +```smart +JavaScript had prototypal inheritance from the beginning. It was one of the core features of the language. -## The "prototype" property - -As we know already, `new F()` creates a new object. - -When a new object is created with `new F()`, the object's `[[Prototype]]` is set to `F.prototype`. - -In other words, if `F` has a `prototype` property with a value of the object type, then `new` operator uses it to set `[[Prototype]]` for the new object. +But in the old times, there was no direct access to it. The only thing that worked reliably was a `"prototype"` property of the constructor function, described in this chapter. So there are many scripts that still use it. +``` Please note that `F.prototype` here means a regular property named `"prototype"` on `F`. It sounds something similar to the term "prototype", but here we really mean a regular property with this name. @@ -42,8 +38,13 @@ That's the resulting picture: ![](proto-constructor-animal-rabbit.png) -On the picture, `"prototype"` is a horizontal arrow, it's a regular property, and `[[Prototype]]` is vertical, meaning the inheritance of `rabbit` from `animal`. +On the picture, `"prototype"` is a horizontal arrow, meaning a regular property, and `[[Prototype]]` is vertical, meaning the inheritance of `rabbit` from `animal`. +```smart header="`F.prototype` only used at `new F` time" +`F.prototype` is only used when `new F` is called, it assigns `[[Prototype]]` of the new object. After that, there's no connection between `F.prototype` and the new object. Think of it as a "one-time gift". + +After the creation, `F.prototype` may change, new objects created by `new F` will have another `[[Prototype]]`, but already existing objects keep the old one. +``` ## Default F.prototype, constructor property diff --git a/1-js/08-prototypes/02-function-prototype/function-prototype-constructor.png b/1-js/08-prototypes/02-function-prototype/function-prototype-constructor.png new file mode 100644 index 00000000..92f80cea Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/function-prototype-constructor.png differ diff --git a/1-js/08-prototypes/02-function-prototype/function-prototype-constructor@2x.png b/1-js/08-prototypes/02-function-prototype/function-prototype-constructor@2x.png new file mode 100644 index 00000000..d8c83f6e Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/function-prototype-constructor@2x.png differ diff --git a/1-js/07-object-oriented-programming/04-function-prototype/native-prototypes-array-tostring.png b/1-js/08-prototypes/02-function-prototype/native-prototypes-array-tostring.png similarity index 100% rename from 1-js/07-object-oriented-programming/04-function-prototype/native-prototypes-array-tostring.png rename to 1-js/08-prototypes/02-function-prototype/native-prototypes-array-tostring.png diff --git a/1-js/07-object-oriented-programming/04-function-prototype/native-prototypes-array-tostring@2x.png b/1-js/08-prototypes/02-function-prototype/native-prototypes-array-tostring@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/04-function-prototype/native-prototypes-array-tostring@2x.png rename to 1-js/08-prototypes/02-function-prototype/native-prototypes-array-tostring@2x.png diff --git a/1-js/07-object-oriented-programming/04-function-prototype/native-prototypes-classes.png b/1-js/08-prototypes/02-function-prototype/native-prototypes-classes.png similarity index 100% rename from 1-js/07-object-oriented-programming/04-function-prototype/native-prototypes-classes.png rename to 1-js/08-prototypes/02-function-prototype/native-prototypes-classes.png diff --git a/1-js/07-object-oriented-programming/04-function-prototype/native-prototypes-classes@2x.png b/1-js/08-prototypes/02-function-prototype/native-prototypes-classes@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/04-function-prototype/native-prototypes-classes@2x.png rename to 1-js/08-prototypes/02-function-prototype/native-prototypes-classes@2x.png diff --git a/1-js/07-object-oriented-programming/04-function-prototype/object-prototype-1.png b/1-js/08-prototypes/02-function-prototype/object-prototype-1.png similarity index 100% rename from 1-js/07-object-oriented-programming/04-function-prototype/object-prototype-1.png rename to 1-js/08-prototypes/02-function-prototype/object-prototype-1.png diff --git a/1-js/07-object-oriented-programming/04-function-prototype/object-prototype-1@2x.png b/1-js/08-prototypes/02-function-prototype/object-prototype-1@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/04-function-prototype/object-prototype-1@2x.png rename to 1-js/08-prototypes/02-function-prototype/object-prototype-1@2x.png diff --git a/1-js/07-object-oriented-programming/04-function-prototype/object-prototype.png b/1-js/08-prototypes/02-function-prototype/object-prototype.png similarity index 100% rename from 1-js/07-object-oriented-programming/04-function-prototype/object-prototype.png rename to 1-js/08-prototypes/02-function-prototype/object-prototype.png diff --git a/1-js/07-object-oriented-programming/04-function-prototype/object-prototype@2x.png b/1-js/08-prototypes/02-function-prototype/object-prototype@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/04-function-prototype/object-prototype@2x.png rename to 1-js/08-prototypes/02-function-prototype/object-prototype@2x.png diff --git a/1-js/08-prototypes/02-function-prototype/proto-constructor-animal-rabbit.png b/1-js/08-prototypes/02-function-prototype/proto-constructor-animal-rabbit.png new file mode 100644 index 00000000..2a745e8f Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/proto-constructor-animal-rabbit.png differ diff --git a/1-js/08-prototypes/02-function-prototype/proto-constructor-animal-rabbit@2x.png b/1-js/08-prototypes/02-function-prototype/proto-constructor-animal-rabbit@2x.png new file mode 100644 index 00000000..609ac141 Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/proto-constructor-animal-rabbit@2x.png differ diff --git a/1-js/07-object-oriented-programming/04-function-prototype/rabbit-animal-object.png b/1-js/08-prototypes/02-function-prototype/rabbit-animal-object.png similarity index 100% rename from 1-js/07-object-oriented-programming/04-function-prototype/rabbit-animal-object.png rename to 1-js/08-prototypes/02-function-prototype/rabbit-animal-object.png diff --git a/1-js/07-object-oriented-programming/04-function-prototype/rabbit-animal-object@2x.png b/1-js/08-prototypes/02-function-prototype/rabbit-animal-object@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/04-function-prototype/rabbit-animal-object@2x.png rename to 1-js/08-prototypes/02-function-prototype/rabbit-animal-object@2x.png diff --git a/1-js/08-prototypes/02-function-prototype/rabbit-prototype-constructor.png b/1-js/08-prototypes/02-function-prototype/rabbit-prototype-constructor.png new file mode 100644 index 00000000..fe71481c Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/rabbit-prototype-constructor.png differ diff --git a/1-js/08-prototypes/02-function-prototype/rabbit-prototype-constructor@2x.png b/1-js/08-prototypes/02-function-prototype/rabbit-prototype-constructor@2x.png new file mode 100644 index 00000000..09f7631f Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/rabbit-prototype-constructor@2x.png differ diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/1-defer-to-prototype/solution.md b/1-js/08-prototypes/03-native-prototypes/1-defer-to-prototype/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/05-native-prototypes/1-defer-to-prototype/solution.md rename to 1-js/08-prototypes/03-native-prototypes/1-defer-to-prototype/solution.md diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/1-defer-to-prototype/task.md b/1-js/08-prototypes/03-native-prototypes/1-defer-to-prototype/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/05-native-prototypes/1-defer-to-prototype/task.md rename to 1-js/08-prototypes/03-native-prototypes/1-defer-to-prototype/task.md diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/2-defer-to-prototype-extended/solution.md b/1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/05-native-prototypes/2-defer-to-prototype-extended/solution.md rename to 1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/solution.md diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/2-defer-to-prototype-extended/task.md b/1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/05-native-prototypes/2-defer-to-prototype-extended/task.md rename to 1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/task.md diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/article.md b/1-js/08-prototypes/03-native-prototypes/article.md similarity index 78% rename from 1-js/07-object-oriented-programming/05-native-prototypes/article.md rename to 1-js/08-prototypes/03-native-prototypes/article.md index f0dac564..a3fdfc6c 100644 --- a/1-js/07-object-oriented-programming/05-native-prototypes/article.md +++ b/1-js/08-prototypes/03-native-prototypes/article.md @@ -121,9 +121,17 @@ String.prototype.show = function() { During the process of development we may have ideas which new built-in methods we'd like to have. And there may be a slight temptation to add them to native prototypes. But that is generally a bad idea. +```warn Prototypes are global, so it's easy to get a conflict. If two libraries add a method `String.prototype.show`, then one of them overwrites the other one. -In modern programming, there is only one case when modifying native prototypes is approved. That's polyfills. In other words, if there's a method in JavaScript specification that is not yet supported by our JavaScript engine (or any of those that we want to support), then we may implement it manually and populate the built-in prototype with it. +So, generally modifying a native prototypeis considered a bad idea. +``` + +**In modern programming, there is only one case when modifying native prototypes is approved. That's polyfilling.** + +Polyfilling is a term for making a substitute for a method that exists in JavaScript specification, but not yet supported by current JavaScript engine . + +Then we may implement it manually and populate the built-in prototype with it. For instance: @@ -134,9 +142,9 @@ if (!String.prototype.repeat) { // if there's no such method String.prototype.repeat = function(n) { // repeat the string n times - // actually, the code should be more complex than that, - // throw errors for negative values of "n" - // the full algorithm is in the specification + // actually, the code should be a little bit more complex than that + // (the full algorithm is in the specification) + // but even an imperfect polyfill is often considered good enough return new Array(n + 1).join(this); }; } @@ -144,32 +152,40 @@ if (!String.prototype.repeat) { // if there's no such method alert( "La".repeat(3) ); // LaLaLa ``` + ## Borrowing from prototypes -In the chapter we talked about method borrowing: +In the chapter we talked about method borrowing. + +That's when we take a method from one object and copy it into another. + +Some methods of native prototypes are often borrowed. + +For instance, if we're making an array-like object, we may want to copy some array methods to it. + +E.g. ```js run -function showArgs() { -*!* - // borrow join from array and call in the context of arguments - alert( [].join.call(arguments, " - ") ); -*/!* -} +let obj = { + 0: "Hello", + 1: "world!", + length: 2, +}; -showArgs("John", "Pete", "Alice"); // John - Pete - Alice +*!* +obj.join = Array.prototype.join; +*/!* + +alert( obj.join(',') ); // Hello,world! ``` -Because `join` resides in `Array.prototype`, we can call it from there directly and rewrite it as: +It works, because the internal algorithm of the built-in `join` method only cares about the correct indexes and the `length` property, it doesn't check that the object is indeed the array. And many built-in methods are like that. -```js -function showArgs() { -*!* - alert( Array.prototype.join.call(arguments, " - ") ); -*/!* -} -``` +Another possibility is to inherit by setting `obj.__proto__` to `Array.prototype`, then all `Array` methods are automatically available in `obj`. -That's more efficient, because it avoids the creation of an extra array object `[]`. On the other hand, it is longer to write. +But that's impossible if `obj` already inherits from another object. Remember, we only can inherit from one object at a time. + +Borrowing methods is flexible, it allows to mix functionality from different objects if needed. ## Summary diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/console_dir_array.png b/1-js/08-prototypes/03-native-prototypes/console_dir_array.png similarity index 100% rename from 1-js/07-object-oriented-programming/05-native-prototypes/console_dir_array.png rename to 1-js/08-prototypes/03-native-prototypes/console_dir_array.png diff --git a/1-js/08-prototypes/03-native-prototypes/function-prototype-constructor.png b/1-js/08-prototypes/03-native-prototypes/function-prototype-constructor.png new file mode 100644 index 00000000..92f80cea Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/function-prototype-constructor.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/function-prototype-constructor@2x.png b/1-js/08-prototypes/03-native-prototypes/function-prototype-constructor@2x.png new file mode 100644 index 00000000..d8c83f6e Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/function-prototype-constructor@2x.png differ diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/native-prototypes-array-tostring.png b/1-js/08-prototypes/03-native-prototypes/native-prototypes-array-tostring.png similarity index 100% rename from 1-js/07-object-oriented-programming/05-native-prototypes/native-prototypes-array-tostring.png rename to 1-js/08-prototypes/03-native-prototypes/native-prototypes-array-tostring.png diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/native-prototypes-array-tostring@2x.png b/1-js/08-prototypes/03-native-prototypes/native-prototypes-array-tostring@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/05-native-prototypes/native-prototypes-array-tostring@2x.png rename to 1-js/08-prototypes/03-native-prototypes/native-prototypes-array-tostring@2x.png diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/native-prototypes-classes.png b/1-js/08-prototypes/03-native-prototypes/native-prototypes-classes.png similarity index 100% rename from 1-js/07-object-oriented-programming/05-native-prototypes/native-prototypes-classes.png rename to 1-js/08-prototypes/03-native-prototypes/native-prototypes-classes.png diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/native-prototypes-classes@2x.png b/1-js/08-prototypes/03-native-prototypes/native-prototypes-classes@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/05-native-prototypes/native-prototypes-classes@2x.png rename to 1-js/08-prototypes/03-native-prototypes/native-prototypes-classes@2x.png diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/object-prototype-1.png b/1-js/08-prototypes/03-native-prototypes/object-prototype-1.png similarity index 100% rename from 1-js/07-object-oriented-programming/05-native-prototypes/object-prototype-1.png rename to 1-js/08-prototypes/03-native-prototypes/object-prototype-1.png diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/object-prototype-1@2x.png b/1-js/08-prototypes/03-native-prototypes/object-prototype-1@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/05-native-prototypes/object-prototype-1@2x.png rename to 1-js/08-prototypes/03-native-prototypes/object-prototype-1@2x.png diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/object-prototype-null.png b/1-js/08-prototypes/03-native-prototypes/object-prototype-null.png similarity index 100% rename from 1-js/07-object-oriented-programming/05-native-prototypes/object-prototype-null.png rename to 1-js/08-prototypes/03-native-prototypes/object-prototype-null.png diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/object-prototype-null@2x.png b/1-js/08-prototypes/03-native-prototypes/object-prototype-null@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/05-native-prototypes/object-prototype-null@2x.png rename to 1-js/08-prototypes/03-native-prototypes/object-prototype-null@2x.png diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/object-prototype.png b/1-js/08-prototypes/03-native-prototypes/object-prototype.png similarity index 100% rename from 1-js/07-object-oriented-programming/05-native-prototypes/object-prototype.png rename to 1-js/08-prototypes/03-native-prototypes/object-prototype.png diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/object-prototype@2x.png b/1-js/08-prototypes/03-native-prototypes/object-prototype@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/05-native-prototypes/object-prototype@2x.png rename to 1-js/08-prototypes/03-native-prototypes/object-prototype@2x.png diff --git a/1-js/08-prototypes/03-native-prototypes/proto-constructor-animal-rabbit.png b/1-js/08-prototypes/03-native-prototypes/proto-constructor-animal-rabbit.png new file mode 100644 index 00000000..2a745e8f Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/proto-constructor-animal-rabbit.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/proto-constructor-animal-rabbit@2x.png b/1-js/08-prototypes/03-native-prototypes/proto-constructor-animal-rabbit@2x.png new file mode 100644 index 00000000..609ac141 Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/proto-constructor-animal-rabbit@2x.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/rabbit-prototype-constructor.png b/1-js/08-prototypes/03-native-prototypes/rabbit-prototype-constructor.png new file mode 100644 index 00000000..fe71481c Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/rabbit-prototype-constructor.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/rabbit-prototype-constructor@2x.png b/1-js/08-prototypes/03-native-prototypes/rabbit-prototype-constructor@2x.png new file mode 100644 index 00000000..09f7631f Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/rabbit-prototype-constructor@2x.png differ diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/2-dictionary-tostring/solution.md b/1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/06-prototype-methods/2-dictionary-tostring/solution.md rename to 1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/solution.md diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/2-dictionary-tostring/task.md b/1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/06-prototype-methods/2-dictionary-tostring/task.md rename to 1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/task.md diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/3-compare-calls/solution.md b/1-js/08-prototypes/04-prototype-methods/3-compare-calls/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/06-prototype-methods/3-compare-calls/solution.md rename to 1-js/08-prototypes/04-prototype-methods/3-compare-calls/solution.md diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/3-compare-calls/task.md b/1-js/08-prototypes/04-prototype-methods/3-compare-calls/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/06-prototype-methods/3-compare-calls/task.md rename to 1-js/08-prototypes/04-prototype-methods/3-compare-calls/task.md diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/article.md b/1-js/08-prototypes/04-prototype-methods/article.md similarity index 64% rename from 1-js/07-object-oriented-programming/06-prototype-methods/article.md rename to 1-js/08-prototypes/04-prototype-methods/article.md index 218b47fe..b1e65a66 100644 --- a/1-js/07-object-oriented-programming/06-prototype-methods/article.md +++ b/1-js/08-prototypes/04-prototype-methods/article.md @@ -1,14 +1,18 @@ -# Methods for prototypes +# Very plain objects, no __proto__ -In this chapter we cover additional methods to work with a prototype. +In the first chapter of this section, we mentioned that there are modern methods to setup a prototype. -There are also other ways to get/set a prototype, besides those that we already know: +The `__proto__` is considered outdated and somewhat deprecated (in browser-only part of the Javascript standard). + +The modern methods are: - [Object.create(proto[, descriptors])](mdn:js/Object/create) -- creates an empty object with given `proto` as `[[Prototype]]` and optional property descriptors. - [Object.getPrototypeOf(obj)](mdn:js/Object/getPrototypeOf) -- returns the `[[Prototype]]` of `obj`. - [Object.setPrototypeOf(obj, proto)](mdn:js/Object/setPrototypeOf) -- sets the `[[Prototype]]` of `obj` to `proto`. +These should be used instead of `__proto__`. + For instance: ```js run @@ -72,7 +76,13 @@ That's for historical reasons. As of now we have all these ways at our disposal. -Technically, we can get/set `[[Prototype]]` at any time. But usually we only set it once at the object creation time, and then do not modify: `rabbit` inherits from `animal`, and that is not going to change. And JavaScript engines are highly optimized to that. Changing a prototype "on-the-fly" with `Object.setPrototypeOf` or `obj.__proto__=` is a very slow operation. But it is possible. +Why `__proto__` was replaced by the functions? That's an interesting question, requiring us to understand why `__proto__` is bad. Read on to get the answer. + +```warn header="Don't reset `[[Prototype]]` unless the speed doesn't matter" +Technically, we can get/set `[[Prototype]]` at any time. But usually we only set it once at the object creation time, and then do not modify: `rabbit` inherits from `animal`, and that is not going to change. + +And JavaScript engines are highly optimized to that. Changing a prototype "on-the-fly" with `Object.setPrototypeOf` or `obj.__proto__=` is a very slow operation, it breaks internal optimizations for object property access operations. So evade it unless you know what you're doing, or Javascript speed totally doesn't matter for you. +``` ## "Very plain" objects @@ -95,7 +105,9 @@ Here if the user types in `__proto__`, the assignment is ignored! That shouldn't surprise us. The `__proto__` property is special: it must be either an object or `null`, a string can not become a prototype. -But we did not intend to implement such behavior, right? We want to store key/value pairs, and the key named `"__proto__"` was not properly saved. So that's a bug. Here the consequences are not terrible. But in other cases the prototype may indeed be changed, so the execution may go wrong in totally unexpected ways. +But we didn't *intend* to implement such behavior, right? We want to store key/value pairs, and the key named `"__proto__"` was not properly saved. So that's a bug! + +Here the consequences are not terrible. But in other cases the prototype may indeed be changed, so the execution may go wrong in totally unexpected ways. What's worst -- usually developers do not think about such possibility at all. That makes such bugs hard to notice and even turn them into vulnerabilities, especially when JavaScript is used on server-side. @@ -111,9 +123,9 @@ The `__proto__` is not a property of an object, but an accessor property of `Obj ![](object-prototype-2.png) -So, if `obj.__proto__` is read or assigned, the corresponding getter/setter is called from its prototype, and it gets/sets `[[Prototype]]`. +So, if `obj.__proto__` is read or set, the corresponding getter/setter is called from its prototype, and it gets/sets `[[Prototype]]`. -As it was said in the beginning: `__proto__` is a way to access `[[Prototype]]`, it is not `[[Prototype]]` itself. +As it was said in the beginning of this tutorial section: `__proto__` is a way to access `[[Prototype]]`, it is not `[[Prototype]]` itself. Now, if we want to use an object as an associative array, we can do it with a little trick: @@ -153,93 +165,31 @@ Please note that most object-related methods are `Object.something(...)`, like ` ```js run let chineseDictionary = Object.create(null); -chineseDictionary.hello = "ni hao"; -chineseDictionary.bye = "zai jian"; +chineseDictionary.hello = "你好"; +chineseDictionary.bye = "再见"; alert(Object.keys(chineseDictionary)); // hello,bye ``` -## Getting all properties - -There are many ways to get keys/values from an object. - -We already know these ones: - -- [Object.keys(obj)](mdn:js/Object/keys) / [Object.values(obj)](mdn:js/Object/values) / [Object.entries(obj)](mdn:js/Object/entries) -- returns an array of enumerable own string property names/values/key-value pairs. These methods only list *enumerable* properties, and those that have *strings as keys*. - -If we want symbolic properties: - -- [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols) -- returns an array of all own symbolic property names. - -If we want non-enumerable properties: - -- [Object.getOwnPropertyNames(obj)](mdn:js/Object/getOwnPropertyNames) -- returns an array of all own string property names. - -If we want *all* properties: - -- [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) -- returns an array of all own property names. - -These methods are a bit different about which properties they return, but all of them operate on the object itself. Properties from the prototype are not listed. - -The `for..in` loop is different: it loops over inherited properties too. - -For instance: - -```js run -let animal = { - eats: true -}; - -let rabbit = { - jumps: true, - __proto__: animal -}; - -*!* -// only own keys -alert(Object.keys(rabbit)); // jumps -*/!* - -*!* -// inherited keys too -for(let prop in rabbit) alert(prop); // jumps, then eats -*/!* -``` - -If we want to distinguish inherited properties, there's a built-in method [obj.hasOwnProperty(key)](mdn:js/Object/hasOwnProperty): it returns `true` if `obj` has its own (not inherited) property named `key`. - -So we can filter out inherited properties (or do something else with them): - -```js run -let animal = { - eats: true -}; - -let rabbit = { - jumps: true, - __proto__: animal -}; - -for(let prop in rabbit) { - let isOwn = rabbit.hasOwnProperty(prop); - alert(`${prop}: ${isOwn}`); // jumps: true, then eats: false -} -``` -Here we have the following inheritance chain: `rabbit`, then `animal`, then `Object.prototype` (because `animal` is a literal object `{...}`, so it's by default), and then `null` above it: - -![](rabbit-animal-object.png) - -Note, there's one funny thing. Where is the method `rabbit.hasOwnProperty` coming from? Looking at the chain we can see that the method is provided by `Object.prototype.hasOwnProperty`. In other words, it's inherited. - -...But why `hasOwnProperty` does not appear in `for..in` loop, if it lists all inherited properties? The answer is simple: it's not enumerable. Just like all other properties of `Object.prototype`. That's why they are not listed. - ## Summary -Here's a brief list of methods we discussed in this chapter -- as a recap: +Modern methods to setup and directly access the prototype are: - [Object.create(proto[, descriptors])](mdn:js/Object/create) -- creates an empty object with given `proto` as `[[Prototype]]` (can be `null`) and optional property descriptors. - [Object.getPrototypeOf(obj)](mdn:js/Object.getPrototypeOf) -- returns the `[[Prototype]]` of `obj` (same as `__proto__` getter). - [Object.setPrototypeOf(obj, proto)](mdn:js/Object.setPrototypeOf) -- sets the `[[Prototype]]` of `obj` to `proto` (same as `__proto__` setter). + +The built-in `__proto__` getter/setter is unsafe if we'd want to put user-generated keys in to an object. Just because a user may enter "__proto__" as the key, and there'll be an error with hopefully easy, but generally unpredictable consequences. + +So we can either use `Object.create(null)` to create a "very plain" object without `__proto__`, or stick to `Map` objects for that. + +Also, `Object.create` provides an easy way to shallow-copy an object with all descriptors: + +```js +let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj)); +``` + + - [Object.keys(obj)](mdn:js/Object/keys) / [Object.values(obj)](mdn:js/Object/values) / [Object.entries(obj)](mdn:js/Object/entries) -- returns an array of enumerable own string property names/values/key-value pairs. - [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols) -- returns an array of all own symbolic property names. - [Object.getOwnPropertyNames(obj)](mdn:js/Object/getOwnPropertyNames) -- returns an array of all own string property names. diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/object-prototype-2.png b/1-js/08-prototypes/04-prototype-methods/object-prototype-2.png similarity index 100% rename from 1-js/07-object-oriented-programming/06-prototype-methods/object-prototype-2.png rename to 1-js/08-prototypes/04-prototype-methods/object-prototype-2.png diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/object-prototype-2@2x.png b/1-js/08-prototypes/04-prototype-methods/object-prototype-2@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/06-prototype-methods/object-prototype-2@2x.png rename to 1-js/08-prototypes/04-prototype-methods/object-prototype-2@2x.png diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/object-prototype-null.png b/1-js/08-prototypes/04-prototype-methods/object-prototype-null.png similarity index 100% rename from 1-js/07-object-oriented-programming/06-prototype-methods/object-prototype-null.png rename to 1-js/08-prototypes/04-prototype-methods/object-prototype-null.png diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/object-prototype-null@2x.png b/1-js/08-prototypes/04-prototype-methods/object-prototype-null@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/06-prototype-methods/object-prototype-null@2x.png rename to 1-js/08-prototypes/04-prototype-methods/object-prototype-null@2x.png diff --git a/1-js/08-prototypes/05-getting-all-properties/article.md b/1-js/08-prototypes/05-getting-all-properties/article.md new file mode 100644 index 00000000..3339066d --- /dev/null +++ b/1-js/08-prototypes/05-getting-all-properties/article.md @@ -0,0 +1,83 @@ + +# Getting all properties + +There are many ways to get keys/values from an object. + +Most of them operate on the object itself, excluding the prototype, let's recall them: + +- [Object.keys(obj)](mdn:js/Object/keys) / [Object.values(obj)](mdn:js/Object/values) / [Object.entries(obj)](mdn:js/Object/entries) -- returns an array of enumerable own string property names/values/key-value pairs. These methods only list *enumerable* properties, and those that have *strings as keys*. + +If we want symbolic properties: + +- [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols) -- returns an array of all own symbolic property names. + +If we want non-enumerable properties: + +- [Object.getOwnPropertyNames(obj)](mdn:js/Object/getOwnPropertyNames) -- returns an array of all own string property names. + +If we want *all* properties: + +- [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) -- returns an array of all own property names. + +These methods are a bit different about which properties they return, but all of them operate on the object itself. Properties from the prototype are not listed. + +## for..in loop + +The `for..in` loop is different: it loops over inherited properties too. + +For instance: + +```js run +let animal = { + eats: true +}; + +let rabbit = { + jumps: true, + __proto__: animal +}; + +*!* +// only own keys +alert(Object.keys(rabbit)); // jumps +*/!* + +*!* +// inherited keys too +for(let prop in rabbit) alert(prop); // jumps, then eats +*/!* +``` + +If that's no what we want, and we'd like to exclude inherited properties, there's a built-in method [obj.hasOwnProperty(key)](mdn:js/Object/hasOwnProperty): it returns `true` if `obj` has its own (not inherited) property named `key`. + +So we can filter out inherited properties (or do something else with them): + +```js run +let animal = { + eats: true +}; + +let rabbit = { + jumps: true, + __proto__: animal +}; + +for(let prop in rabbit) { + let isOwn = rabbit.hasOwnProperty(prop); + alert(`${prop}: ${isOwn}`); // jumps: true, then eats: false +} +``` + +Here we have the following inheritance chain: `rabbit`, then `animal`, then `Object.prototype` (because `animal` is a literal object `{...}`, so it's by default), and then `null` above it: + +![](rabbit-animal-object.png) + +Note, there's one funny thing. Where is the method `rabbit.hasOwnProperty` coming from? Looking at the chain we can see that the method is provided by `Object.prototype.hasOwnProperty`. In other words, it's inherited. + +...But why `hasOwnProperty` does not appear in `for..in` loop, if it lists all inherited properties? The answer is simple: it's not enumerable. Just like all other properties of `Object.prototype`. That's why they are not listed. + +## Summary + +Most methods ignore inherited properties, with a notable exception of `for..in`. + +For the latter we can use [obj.hasOwnProperty(key)](mdn:js/Object/hasOwnProperty): it returns `true` if `obj` has its own (not inherited) property named `key`. diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/rabbit-animal-object.png b/1-js/08-prototypes/05-getting-all-properties/rabbit-animal-object.png similarity index 100% rename from 1-js/07-object-oriented-programming/06-prototype-methods/rabbit-animal-object.png rename to 1-js/08-prototypes/05-getting-all-properties/rabbit-animal-object.png diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/rabbit-animal-object@2x.png b/1-js/08-prototypes/05-getting-all-properties/rabbit-animal-object@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/06-prototype-methods/rabbit-animal-object@2x.png rename to 1-js/08-prototypes/05-getting-all-properties/rabbit-animal-object@2x.png diff --git a/1-js/08-prototypes/index.md b/1-js/08-prototypes/index.md new file mode 100644 index 00000000..8554a0e3 --- /dev/null +++ b/1-js/08-prototypes/index.md @@ -0,0 +1 @@ +# Prototypes, inheritance diff --git a/1-js/07-object-oriented-programming/08-class-patterns/1-inheritance-error-assign/solution.md b/1-js/09-classes/01-class-patterns/1-inheritance-error-assign/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/08-class-patterns/1-inheritance-error-assign/solution.md rename to 1-js/09-classes/01-class-patterns/1-inheritance-error-assign/solution.md diff --git a/1-js/07-object-oriented-programming/08-class-patterns/1-inheritance-error-assign/task.md b/1-js/09-classes/01-class-patterns/1-inheritance-error-assign/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/08-class-patterns/1-inheritance-error-assign/task.md rename to 1-js/09-classes/01-class-patterns/1-inheritance-error-assign/task.md diff --git a/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/solution.md b/1-js/09-classes/01-class-patterns/2-rewrite-to-prototypes/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/solution.md rename to 1-js/09-classes/01-class-patterns/2-rewrite-to-prototypes/solution.md diff --git a/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/solution.view/clock.js b/1-js/09-classes/01-class-patterns/2-rewrite-to-prototypes/solution.view/clock.js similarity index 68% rename from 1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/solution.view/clock.js rename to 1-js/09-classes/01-class-patterns/2-rewrite-to-prototypes/solution.view/clock.js index c0363448..bdf7bb72 100644 --- a/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/solution.view/clock.js +++ b/1-js/09-classes/01-class-patterns/2-rewrite-to-prototypes/solution.view/clock.js @@ -1,8 +1,8 @@ function Clock({ template }) { - this._template = template; + this.template = template; } -Clock.prototype._render = function() { +Clock.prototype.render = function() { let date = new Date(); let hours = date.getHours(); @@ -14,7 +14,7 @@ Clock.prototype._render = function() { let secs = date.getSeconds(); if (secs < 10) secs = '0' + secs; - let output = this._template + let output = this.template .replace('h', hours) .replace('m', mins) .replace('s', secs); @@ -23,10 +23,10 @@ Clock.prototype._render = function() { }; Clock.prototype.stop = function() { - clearInterval(this._timer); + clearInterval(this.timer); }; Clock.prototype.start = function() { - this._render(); - this._timer = setInterval(() => this._render(), 1000); + this.render(); + this.timer = setInterval(() => this.render(), 1000); }; diff --git a/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/solution.view/index.html b/1-js/09-classes/01-class-patterns/2-rewrite-to-prototypes/solution.view/index.html similarity index 100% rename from 1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/solution.view/index.html rename to 1-js/09-classes/01-class-patterns/2-rewrite-to-prototypes/solution.view/index.html diff --git a/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/source.view/clock.js b/1-js/09-classes/01-class-patterns/2-rewrite-to-prototypes/source.view/clock.js similarity index 100% rename from 1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/source.view/clock.js rename to 1-js/09-classes/01-class-patterns/2-rewrite-to-prototypes/source.view/clock.js diff --git a/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/source.view/index.html b/1-js/09-classes/01-class-patterns/2-rewrite-to-prototypes/source.view/index.html similarity index 100% rename from 1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/source.view/index.html rename to 1-js/09-classes/01-class-patterns/2-rewrite-to-prototypes/source.view/index.html diff --git a/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/task.md b/1-js/09-classes/01-class-patterns/2-rewrite-to-prototypes/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/task.md rename to 1-js/09-classes/01-class-patterns/2-rewrite-to-prototypes/task.md diff --git a/1-js/07-object-oriented-programming/08-class-patterns/article.md b/1-js/09-classes/01-class-patterns/article.md similarity index 96% rename from 1-js/07-object-oriented-programming/08-class-patterns/article.md rename to 1-js/09-classes/01-class-patterns/article.md index 92b521c1..837941cb 100644 --- a/1-js/07-object-oriented-programming/08-class-patterns/article.md +++ b/1-js/09-classes/01-class-patterns/article.md @@ -7,9 +7,9 @@ In object-oriented programming, a *class* is an extensible program-code-template There's a special syntax construct and a keyword `class` in JavaScript. But before studying it, we should consider that the term "class" comes from the theory of object-oriented programming. The definition is cited above, and it's language-independent. -In JavaScript there are several well-known programming patterns to make classes even without using the `class` keyword. And here we'll talk about them first. +In JavaScript there are several well-known programming patterns to make classes even without using the `class` keyword. People talk about "classes" meaning no only those defined with `class`, but also with these patterns. -The `class` construct will be described in the next chapter, but in JavaScript it's a "syntax sugar" and an extension of one of the patterns that we'll study here. +The `class` construct will be described in the next chapter, but in JavaScript it's a "syntax sugar" and an extension of the prototypal class pattern described here. ## Functional class pattern diff --git a/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal-2.png b/1-js/09-classes/01-class-patterns/class-inheritance-rabbit-animal-2.png similarity index 100% rename from 1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal-2.png rename to 1-js/09-classes/01-class-patterns/class-inheritance-rabbit-animal-2.png diff --git a/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal-2@2x.png b/1-js/09-classes/01-class-patterns/class-inheritance-rabbit-animal-2@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal-2@2x.png rename to 1-js/09-classes/01-class-patterns/class-inheritance-rabbit-animal-2@2x.png diff --git a/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal.png b/1-js/09-classes/01-class-patterns/class-inheritance-rabbit-animal.png similarity index 100% rename from 1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal.png rename to 1-js/09-classes/01-class-patterns/class-inheritance-rabbit-animal.png diff --git a/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal@2x.png b/1-js/09-classes/01-class-patterns/class-inheritance-rabbit-animal@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal@2x.png rename to 1-js/09-classes/01-class-patterns/class-inheritance-rabbit-animal@2x.png diff --git a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-1.png b/1-js/09-classes/01-class-patterns/rabbit-animal-independent-1.png similarity index 100% rename from 1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-1.png rename to 1-js/09-classes/01-class-patterns/rabbit-animal-independent-1.png diff --git a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-1@2x.png b/1-js/09-classes/01-class-patterns/rabbit-animal-independent-1@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-1@2x.png rename to 1-js/09-classes/01-class-patterns/rabbit-animal-independent-1@2x.png diff --git a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-2.png b/1-js/09-classes/01-class-patterns/rabbit-animal-independent-2.png similarity index 100% rename from 1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-2.png rename to 1-js/09-classes/01-class-patterns/rabbit-animal-independent-2.png diff --git a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-2@2x.png b/1-js/09-classes/01-class-patterns/rabbit-animal-independent-2@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-2@2x.png rename to 1-js/09-classes/01-class-patterns/rabbit-animal-independent-2@2x.png diff --git a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/solution.md b/1-js/09-classes/02-class/1-rewrite-to-class/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/solution.md rename to 1-js/09-classes/02-class/1-rewrite-to-class/solution.md diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/source.view/clock.js b/1-js/09-classes/02-class/1-rewrite-to-class/solution.view/clock.js similarity index 70% rename from 1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/source.view/clock.js rename to 1-js/09-classes/02-class/1-rewrite-to-class/solution.view/clock.js index 8009273e..d701c0ca 100644 --- a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/source.view/clock.js +++ b/1-js/09-classes/02-class/1-rewrite-to-class/solution.view/clock.js @@ -1,9 +1,9 @@ class Clock { constructor({ template }) { - this._template = template; + this.template = template; } - _render() { + render() { let date = new Date(); let hours = date.getHours(); @@ -15,7 +15,7 @@ class Clock { let secs = date.getSeconds(); if (secs < 10) secs = '0' + secs; - let output = this._template + let output = this.template .replace('h', hours) .replace('m', mins) .replace('s', secs); @@ -24,11 +24,11 @@ class Clock { } stop() { - clearInterval(this._timer); + clearInterval(this.timer); } start() { - this._render(); - this._timer = setInterval(() => this._render(), 1000); + this.render(); + this.timer = setInterval(() => this.render(), 1000); } } diff --git a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/solution.view/index.html b/1-js/09-classes/02-class/1-rewrite-to-class/solution.view/index.html similarity index 100% rename from 1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/solution.view/index.html rename to 1-js/09-classes/02-class/1-rewrite-to-class/solution.view/index.html diff --git a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/source.view/clock.js b/1-js/09-classes/02-class/1-rewrite-to-class/source.view/clock.js similarity index 69% rename from 1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/source.view/clock.js rename to 1-js/09-classes/02-class/1-rewrite-to-class/source.view/clock.js index d312c93e..537f7268 100644 --- a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/source.view/clock.js +++ b/1-js/09-classes/02-class/1-rewrite-to-class/source.view/clock.js @@ -1,10 +1,10 @@ function Clock({ template }) { - this._template = template; + this.template = template; } -Clock.prototype._render = function() { +Clock.prototype.render = function() { let date = new Date(); let hours = date.getHours(); @@ -16,7 +16,7 @@ Clock.prototype._render = function() { let secs = date.getSeconds(); if (secs < 10) secs = '0' + secs; - let output = this._template + let output = this.template .replace('h', hours) .replace('m', mins) .replace('s', secs); @@ -25,10 +25,10 @@ Clock.prototype._render = function() { }; Clock.prototype.stop = function() { - clearInterval(this._timer); + clearInterval(this.timer); }; Clock.prototype.start = function() { - this._render(); - this._timer = setInterval(() => this._render(), 1000); + this.render(); + this.timer = setInterval(() => this.render(), 1000); }; diff --git a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/source.view/index.html b/1-js/09-classes/02-class/1-rewrite-to-class/source.view/index.html similarity index 100% rename from 1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/source.view/index.html rename to 1-js/09-classes/02-class/1-rewrite-to-class/source.view/index.html diff --git a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/task.md b/1-js/09-classes/02-class/1-rewrite-to-class/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/task.md rename to 1-js/09-classes/02-class/1-rewrite-to-class/task.md diff --git a/1-js/09-classes/02-class/article.md b/1-js/09-classes/02-class/article.md new file mode 100644 index 00000000..2f2bee86 --- /dev/null +++ b/1-js/09-classes/02-class/article.md @@ -0,0 +1,272 @@ + +# Classes + +The "class" construct allows to define prototype-based classes with a clean, nice-looking syntax. It also introduces new great features, useful for object-oriented programming. + +## The "class" syntax + +The `class` syntax is versatile, we'll start with a simple example first. + +Here's a prototype-based class `User`: + +```js run +function User(name) { + this.name = name; +} + +User.prototype.sayHi = function() { + alert(this.name); +} + +let user = new User("John"); +user.sayHi(); +``` + +...And that's the same using `class` syntax: + +```js run +class User { + + constructor(name) { + this.name = name; + } + + sayHi() { + alert(this.name); + } + +} + +let user = new User("John"); +user.sayHi(); +``` + +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. Novice 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. + +## What is a class? + +So, what exactly is a `class`? We may think that it defines a new language-level entity, but that would be wrong. + +In Javascript, a class is a kind of a function. + +The definition `class User {...}` create such function and puts the methods into `User.prototype`. So the structure is similar. + +Here's the code to dig into the class and see that: + +```js run +class User { + constructor(name) { this.name = name; } + sayHi() { alert(this.name); } +} + +*!* +// proof: User is a function +alert(typeof User); // function +*/!* + +*!* +// proof: User is the "constructor" function +*/!* +alert(User === User.prototype.constructor); // true + +*!* +// proof: there are two methods in its "prototype" +*/!* +alert(Object.getOwnPropertyNames(User.prototype)); // constructor, sayHi +``` + +Here's the illustration of what `class User` creates: + +![](class-user.png) + +So `class` is a special syntax to define a constructor together with its prototype methods. There are also quite a few additional features here and there, we'll study them later on. + +## Class Expression + +Just like functions, classes can be defined inside another expression, passed around, returned etc. + +Here's a class-returning function ("class factory"): + +```js run +function makeClass(phrase) { +*!* + // declare a class and return it + return class { + sayHi() { + alert(phrase); + }; + }; +*/!* +} + +let User = makeClass("Hello"); + +new User().sayHi(); // Hello +``` + +That's quite normal if we recall that `class` is just a special form of a function-with-prototype definition. + +And, like Named Function Expressions, such classes also may have a name, that is visible inside that class only: + +```js run +// "Named Class Expression" (alas, no such term, but that's what's going on) +let User = class *!*MyClass*/!* { + sayHi() { + alert(MyClass); // MyClass is visible only inside the class + } +}; + +new User().sayHi(); // works, shows MyClass definition + +alert(MyClass); // error, MyClass not visible outside of the class +``` + +## Differences of classes vs functions + +Classes have some differences compared to regular functions: + +Constructors require `new` +: Unlike a regular function, a class `constructor` can't be called without `new`: + +```js run +class User { + constructor() {} +} + +alert(typeof User); // function +User(); // Error: Class constructor User cannot be invoked without 'new' +``` + +Different string output +: If we output it like `alert(User)`, some engines show `"class User..."`, while others show `"function User..."`. + +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. + +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. + +Classes have a default `constructor() {}` +: If there's no `constructor` in the `class` construct, then an empty function is generated, same as if we had written `constructor() {}`. + +Classes always `use strict` +: All code inside the class construct is automatically in strict mode. + + +## Getters/setters, other shorthands + +Classes also 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; + } + +*!* + 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 + +user = new User(""); // Name too short. +``` + +Internally, getters and setters are created on `User.prototype`, like this: + +```js +Object.defineProperties(User.prototype, { + name: { + get() { + return this._name + }, + set(name) { + // ... + } + } +}); +``` + +Here's an example with computed properties: + +```js run +function f() { return "sayHi"; } + +class User { + [f()]() { + alert("Hello"); + } + +} + +new User().sayHi(); +``` + +For a generator method, similarly, prepend it with `*`. + +## Class properties + +```warn header="Old browsers may need a polyfill" +Class-level properties is a recent addition to the language. +``` + +In the example before, `User` only had methods. Let's add a property: + +```js run +class User { + name = "Anonymous"; + + sayHi() { + alert(`Hello, ${this.name}!`); + } +} + +new User().sayHi(); +``` + +The property is not placed into `User.prototype`. Instead, it is created by `new`, separately for every object. So, the property will never be shared between different objects of the same class. + + +## Summary + +The basic class syntax looks like this: + +```js +class MyClass { + prop = value; + + constructor(...) { + // ... + } + + method(...) {} + + get something(...) {} + set something(...) {} + + [Symbol.iterator]() {} + // ... +} +``` + +`MyClass` is technically a function, while methods are written to `MyClass.prototype`. + +In the next chapters we'll learn more about classes, including inheritance and other features. diff --git a/1-js/07-object-oriented-programming/09-class/class-user.png b/1-js/09-classes/02-class/class-user.png similarity index 100% rename from 1-js/07-object-oriented-programming/09-class/class-user.png rename to 1-js/09-classes/02-class/class-user.png diff --git a/1-js/07-object-oriented-programming/09-class/class-user@2x.png b/1-js/09-classes/02-class/class-user@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/09-class/class-user@2x.png rename to 1-js/09-classes/02-class/class-user@2x.png diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/1-class-constructor-error/solution.md b/1-js/09-classes/03-class-inheritance/1-class-constructor-error/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/1-class-constructor-error/solution.md rename to 1-js/09-classes/03-class-inheritance/1-class-constructor-error/solution.md diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/1-class-constructor-error/task.md b/1-js/09-classes/03-class-inheritance/1-class-constructor-error/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/1-class-constructor-error/task.md rename to 1-js/09-classes/03-class-inheritance/1-class-constructor-error/task.md diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.md b/1-js/09-classes/03-class-inheritance/2-clock-class-extended/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.md rename to 1-js/09-classes/03-class-inheritance/2-clock-class-extended/solution.md diff --git a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/solution.view/clock.js b/1-js/09-classes/03-class-inheritance/2-clock-class-extended/solution.view/clock.js similarity index 100% rename from 1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/solution.view/clock.js rename to 1-js/09-classes/03-class-inheritance/2-clock-class-extended/solution.view/clock.js diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js b/1-js/09-classes/03-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js rename to 1-js/09-classes/03-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/index.html b/1-js/09-classes/03-class-inheritance/2-clock-class-extended/solution.view/index.html similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/index.html rename to 1-js/09-classes/03-class-inheritance/2-clock-class-extended/solution.view/index.html diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/clock.js b/1-js/09-classes/03-class-inheritance/2-clock-class-extended/source.view/clock.js similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/clock.js rename to 1-js/09-classes/03-class-inheritance/2-clock-class-extended/source.view/clock.js diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/source.view/index.html b/1-js/09-classes/03-class-inheritance/2-clock-class-extended/source.view/index.html similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/source.view/index.html rename to 1-js/09-classes/03-class-inheritance/2-clock-class-extended/source.view/index.html diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/task.md b/1-js/09-classes/03-class-inheritance/2-clock-class-extended/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/task.md rename to 1-js/09-classes/03-class-inheritance/2-clock-class-extended/task.md diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/rabbit-extends-object.png b/1-js/09-classes/03-class-inheritance/3-class-extend-object/rabbit-extends-object.png similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/rabbit-extends-object.png rename to 1-js/09-classes/03-class-inheritance/3-class-extend-object/rabbit-extends-object.png diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/rabbit-extends-object@2x.png b/1-js/09-classes/03-class-inheritance/3-class-extend-object/rabbit-extends-object@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/rabbit-extends-object@2x.png rename to 1-js/09-classes/03-class-inheritance/3-class-extend-object/rabbit-extends-object@2x.png diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/solution.md b/1-js/09-classes/03-class-inheritance/3-class-extend-object/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/solution.md rename to 1-js/09-classes/03-class-inheritance/3-class-extend-object/solution.md diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/task.md b/1-js/09-classes/03-class-inheritance/3-class-extend-object/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/task.md rename to 1-js/09-classes/03-class-inheritance/3-class-extend-object/task.md diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-extends.png b/1-js/09-classes/03-class-inheritance/animal-rabbit-extends.png similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-extends.png rename to 1-js/09-classes/03-class-inheritance/animal-rabbit-extends.png diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-extends@2x.png b/1-js/09-classes/03-class-inheritance/animal-rabbit-extends@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-extends@2x.png rename to 1-js/09-classes/03-class-inheritance/animal-rabbit-extends@2x.png diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/article.md b/1-js/09-classes/03-class-inheritance/article.md similarity index 73% rename from 1-js/07-object-oriented-programming/10-class-inheritance/article.md rename to 1-js/09-classes/03-class-inheritance/article.md index 314c4d33..1541a965 100644 --- a/1-js/07-object-oriented-programming/10-class-inheritance/article.md +++ b/1-js/09-classes/03-class-inheritance/article.md @@ -1,5 +1,5 @@ -# Class inheritance, super +# Class inheritance Classes can extend one another. There's a nice syntax, technically based on the prototypal inheritance. @@ -429,147 +429,3 @@ let rabbit = { rabbit.eat(); // Error calling super (because there's no [[HomeObject]]) */!* ``` - -## Static methods and inheritance - -The `class` syntax supports inheritance for static properties too. - -For instance: - -```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`. - - -![](animal-rabbit-static.png) - -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`. - -### No static inheritance in built-ins - -Please note that built-in classes don't have such static `[[Prototype]]` reference. For instance, `Object` has `Object.defineProperty`, `Object.keys` and so on, but `Array`, `Date` etc do not inherit them. - -Here's the picture structure for `Date` and `Object`: - -![](object-date-inheritance.png) - -Note, there's no link between `Date` and `Object`. Both `Object` and `Date` exist independently. `Date.prototype` inherits from `Object.prototype`, but that's all. - -Such difference exists for historical reasons: there was no thought about class syntax and inheriting static methods at the dawn of JavaScript language. - -## Natives are extendable - -Built-in classes like Array, Map and others are extendable also. - -For instance, here `PowerArray` inherits from the native `Array`: - -```js run -// add one more method to it (can do more) -class PowerArray extends Array { - isEmpty() { - return this.length === 0; - } -} - -let arr = new PowerArray(1, 2, 5, 10, 50); -alert(arr.isEmpty()); // false - -let filteredArr = arr.filter(item => item >= 10); -alert(filteredArr); // 10, 50 -alert(filteredArr.isEmpty()); // false -``` - -Please note one very interesting thing. Built-in methods like `filter`, `map` and others -- return new objects of exactly the inherited type. They rely on the `constructor` property to do so. - -In the example above, -```js -arr.constructor === PowerArray -``` - -So when `arr.filter()` is called, it internally creates the new array of results exactly as `new PowerArray`. And we can keep using its methods further down the chain. - -Even more, we can customize that behavior. The static getter `Symbol.species`, if exists, returns the constructor to use in such cases. - -For example, here due to `Symbol.species` built-in methods like `map`, `filter` will return "normal" arrays: - -```js run -class PowerArray extends Array { - isEmpty() { - return this.length === 0; - } - -*!* - // built-in methods will use this as the constructor - static get [Symbol.species]() { - return Array; - } -*/!* -} - -let arr = new PowerArray(1, 2, 5, 10, 50); -alert(arr.isEmpty()); // false - -// filter creates new array using arr.constructor[Symbol.species] as constructor -let filteredArr = arr.filter(item => item >= 10); - -*!* -// filteredArr is not PowerArray, but Array -*/!* -alert(filteredArr.isEmpty()); // Error: filteredArr.isEmpty is not a function -``` - -We can use it in more advanced keys to strip extended functionality from resulting values if not needed. Or, maybe, to extend it even further. diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-array-object.png b/1-js/09-classes/03-class-inheritance/class-inheritance-array-object.png similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-array-object.png rename to 1-js/09-classes/03-class-inheritance/class-inheritance-array-object.png diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-array-object@2x.png b/1-js/09-classes/03-class-inheritance/class-inheritance-array-object@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-array-object@2x.png rename to 1-js/09-classes/03-class-inheritance/class-inheritance-array-object@2x.png diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-animal.png b/1-js/09-classes/03-class-inheritance/class-inheritance-rabbit-animal.png similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-animal.png rename to 1-js/09-classes/03-class-inheritance/class-inheritance-rabbit-animal.png diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-animal@2x.png b/1-js/09-classes/03-class-inheritance/class-inheritance-rabbit-animal@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-animal@2x.png rename to 1-js/09-classes/03-class-inheritance/class-inheritance-rabbit-animal@2x.png diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-run-animal.png b/1-js/09-classes/03-class-inheritance/class-inheritance-rabbit-run-animal.png similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-run-animal.png rename to 1-js/09-classes/03-class-inheritance/class-inheritance-rabbit-run-animal.png diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-run-animal@2x.png b/1-js/09-classes/03-class-inheritance/class-inheritance-rabbit-run-animal@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-run-animal@2x.png rename to 1-js/09-classes/03-class-inheritance/class-inheritance-rabbit-run-animal@2x.png diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/this-super-loop.png b/1-js/09-classes/03-class-inheritance/this-super-loop.png similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/this-super-loop.png rename to 1-js/09-classes/03-class-inheritance/this-super-loop.png diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/this-super-loop@2x.png b/1-js/09-classes/03-class-inheritance/this-super-loop@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/this-super-loop@2x.png rename to 1-js/09-classes/03-class-inheritance/this-super-loop@2x.png diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-static.png b/1-js/09-classes/04-static-properties-methods/animal-rabbit-static.png similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-static.png rename to 1-js/09-classes/04-static-properties-methods/animal-rabbit-static.png diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-static@2x.png b/1-js/09-classes/04-static-properties-methods/animal-rabbit-static@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-static@2x.png rename to 1-js/09-classes/04-static-properties-methods/animal-rabbit-static@2x.png diff --git a/1-js/09-classes/04-static-properties-methods/article.md b/1-js/09-classes/04-static-properties-methods/article.md new file mode 100644 index 00000000..760641ea --- /dev/null +++ b/1-js/09-classes/04-static-properties-methods/article.md @@ -0,0 +1,226 @@ + +# Static properties and methods + +We can also assign a methods 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 inhereted, we can access `Parent.method` as `Child.method`. + +For instance, `Animal.compare` in the code below is inhereted 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`. + + +![](animal-rabbit-static.png) + +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`. diff --git a/1-js/09-classes/05-private-protected-properties-methods/article.md b/1-js/09-classes/05-private-protected-properties-methods/article.md new file mode 100644 index 00000000..7af04797 --- /dev/null +++ b/1-js/09-classes/05-private-protected-properties-methods/article.md @@ -0,0 +1,330 @@ + +# Private and protected properties and methods + +One of the most important principles of object oriented programming -- delimiting internal interface from the external one. + +That is "a must" practice in developing anything more complex than a "hello world" app. + +To understand this, let's break away from development and turn our eyes into the real world. + +Usually, devices that we're using are quite complex. But delimiting the internal interface from the external one allows to use them without problems. + +## A real-life example + +For instance, a coffee machine. Simple from outside: a button, a display, a few holes...And, surely, the result -- great coffee! :) + +![](coffee.jpg) + +But inside... (a picture from the repair manual) + +![](coffee-inside.jpg) + +A lot of details. But we can use it without knowing anything. + +Coffee machines are quite reliable, aren't they? We can use one for years, and only if something goes wrong -- bring it for repairs. + +The secret of reliability and simplicity of a coffee machine -- all details are well-tuned and *hidden* inside. + +If we remove the protective cover from the coffee machine, then using it will be much more complex (where to press?), and dangerous (it can electrocute). + +As we'll see, in programming objects are like coffee machines. + +But in order to hide inner details, we'll use not a protective cover, but rather special syntax of the language and conventions. + +## Internal and external interface + +In object-oriented programming, properties and methods are split into two groups: + +- *Internal interface* -- methods and properties, accessible from other methods of the class, but not from the outside. +- *External interface* -- methods and properties, accessible also from outside the class. + +If we continue the analogy with the coffee machine -- what's hidden inside: a boiler tube, heating element, and so on -- is its internal interface. + +An internal interface is used for the object to work, its details use each other. For instance, a boiler tube is attached to the heating element. + +But from the outside a coffee machine is closed by the protective cover, so that no one can reach those. Details are hidden and inaccessible. We can use its features via the external interface. + +So, all we need to use an object is to know its external interface. We may be completely unaware how it works inside, and that's great. + +That was a general introduction. + +In JavaScript, there are three types of properties and members: + +- Public: accessible from anywhere. They comprise the external interface. Till now we were only using public properties and methods. +- Private: accessible only from inside the class. These are for the internal interface. + +In many other languages there also exist "protected" fields: accessible only from inside the class and those extending it. They are also useful for the internal interface. They are in a sense more widespread than private ones, because we usually want inheriting classes to gain access to properly do the extension. + +Protected fields are not implemented in Javascript on the language level, but in practice they are very convenient, so they are emulated. + +In the next step we'll make a coffee machine in Javascript with all these types of properties. A coffee machine has a lot of details, we won't model them to stay simple (though we could). + +## Protecting "waterAmount" + +Let's make a simple coffee machine class first: + +```js run +class CoffeeMachine { + waterAmount = 0; // the amount of water inside + + constructor(power) { + this.power = power; + alert( `Created a coffee-machine, power: ${power}` ); + } + +} + +// create the coffee machine +let coffeeMachine = new CoffeeMachine(100); + +// add water +coffeeMachine.waterAmount = 200; +``` + +Right now the properties `waterAmount` and `power` are public. We can easily get/set them from the outside to any value. + +Let's change `waterAmount` property to protected to have more control over it. For instance, we don't want anyone to set it below zero. + +**Protected properties are usually prefixed with an underscore `_`.** + +That is not enforced on the language level, but there's a convention that such properties and methods should not be accessed from the outside. Most programmers follow it. + +So our property will be called `_waterAmount`: + +```js run +class CoffeeMachine { + _waterAmount = 0; + + set waterAmount(value) { + if (value < 0) throw new Error("Negative water"); + this._waterAmount = value; + } + + get waterAmount() { + return this.waterAmount; + } + + constructor(power) { + this._power = power; + } + +} + +// create the coffee machine +let coffeeMachine = new CoffeeMachine(100); + +// add water +coffeeMachine.waterAmount = -10; // Error: Negative water +``` + +Now the access is under control, so setting the water below zero fails. + +## Read-only "power" + +For `power` property, let's make it read-only. It sometimes happens that a property must be set at creation time only, and then never modified. + +That's exactly the case for a coffee machine: power never changes. + +To do so, we only need to make getter, but not the setter: + +```js run +class CoffeeMachine { + // ... + + constructor(power) { + this._power = power; + } + + get power() { + return this._power; + } + +} + +// create the coffee machine +let coffeeMachine = new CoffeeMachine(100); + +alert(`Power is: ${coffeeMachine.power}W`); // Power is: 100W + +coffeeMachine.power = 25; // Error (no setter) +``` + +````smart header="Getter/setter functions" +Here we used getter/setter syntax. + +But most of the time `get.../set...` functions are preferred, like this: + +```js +class CoffeeMachine { + _waterAmount = 0; + + *!*setWaterAmount(value)*/!* { + if (value < 0) throw new Error("Negative water"); + this._waterAmount = value; + } + + *!*getWaterAmount()*/!* { + return this.waterAmount; + } +} + +new CoffeeMachine().setWaterAmount(100); +``` + +That looks a bit longer, but functions are more flexible. They can accept multiple arguments (even if we don't need them right now). So, for the future, just in case we need to refactor something, functions are a safer choise. + +Surely, there's a tradeoff. On the other hand, get/set syntax is shorter, so ultimately there's no strict rule, it's up to you to decide. +```` + +```smart header="Protected fields are inherited" +If we inherit `class MegaMachine extends CoffeeMachine`, then nothing prevents us from accessing `this._waterAmount` or `this._power` from the methods of the new class. + +So protected fields are naturally inheritable. Unlike private ones that we'll see below. +``` + +## Private "#waterLimit" + +[recent browser=none] + +There's a finished Javascript proposal, almost in the standard, that provides language-level support for private properties and methods. + +Privates should start with `#`. They are only accessible from inside the class. + +For instance, here we add a private `#waterLimit` property and extract the water-checking logic into a separate method: + +```js +class CoffeeMachine { +*!* + #waterLimit = 200; +*/!* + +*!* + #checkWater(water) { + if (value < 0) throw new Error("Negative water"); + if (value > this.#waterLimit) throw new Error("Too much water"); + } +*/!* + + _waterAmount = 0; + + set waterAmount(value) { +*!* + this.#checkWater(value); +*/!* + this._waterAmount = value; + } + + get waterAmount() { + return this.waterAmount; + } + +} + +let coffeeMachine = new CoffeeMachine(); + +*!* +coffeeMachine.#checkWater(); // Error +coffeeMachine.#waterLimit = 1000; // Error +*/!* + +coffeeMachine.waterAmount = 100; // Works +``` + +On the language level, `#` is a special sign that the field is private. We can't access it from outside or from inhereting classes. + +Private fields do not conflict with public ones. We can have both private `#waterAmount` and public `waterAmount` fields at the same time. + +For instance, let's make `waterAmount` an accessor for `#waterAmount`: + +```js run +class CoffeeMachine { + + #waterAmount = 0; + + get waterAmount() { + return this.#waterAmount; + } + + set waterAmount(value) { + if (value < 0) throw new Error("Negative water"); + this.#waterAmount = value; + } +} + +let machine = new CoffeeMachine(); + +machine.waterAmount = 100; +alert(machine.#waterAmount); // Error +``` + +Unlike protected ones, private fields are enforced by the language itselfs. That's a good thing. + +But if we inherit from `CoffeeMachine`, then we'll have no direct access to `#waterAmount`. We'll need to rely on `waterAmount` getter/setter: + +```js +class CoffeeMachine extends CoffeeMachine() { + method() { +*!* + alert( this.#waterAmount ); // Error: can only access from CoffeeMachine +*/!* + } +} +``` + +In many scenarios such limitation is too severe. If we extend a `CoffeeMachine`, we may have legitimate reason to access its internals. That's why protected fields are used most of the time, even though they are not supported by the language syntax. + +````warn +Private fields are special. + +Remember, usually we can access fields by this[name]: + +```js +class User { + ... + sayHi() { + let fieldName = "name"; + alert(`Hello, ${this[fieldName]}`); + } +} +``` + +With private fields that's impossible: `this['#name']` doesn't work. That's a syntax limitation to ensure privacy. +```` + +## Summary + +In terms of OOP, delimiting of the internal interface from the external one is called [encapsulation]("https://en.wikipedia.org/wiki/Encapsulation_(computer_programming)"). + +It gives the following benefits: + +Protection for users, so that they don't shoot themselves in the feet +: Imagine, there's a team of developers using a coffee machine. It was made by the "Best CoffeeMachine" company, and works fine, but a protective cover was removed. So the internal interface is exposed. + + All developers are civilized -- they use the coffee machine as intended. But one of them, John, decided that he's the smartest one, and made some tweaks in the coffee machine internals. So the coffee machine failed two days later. + + That's surely not John's fault, but rather the person who removed the protective cover and let John do his manipulations. + + The same in programming. If a user of a class will change things not intended to be changed from the outside -- the consequences are unpredictable. + +Supportable +: The situation in programming is more complex than with a real-life coffee machine, because we don't just buy it once. The code constantly undergoes development and improvement. + + **If we strictly delimit the internal interface, then the developer of the class can freely change its internal properties and methods, even without informing the users..** + + It's much easier to develop, if you know that certain methods can be renamed, their parameters can be changed, and even removed, because no external code depends on them. + + For users, when a new version comes out, it may be a total overhaul, but still simple to upgrade if the external interface is the same. + +Hiding complexity +: People adore to use things that are simple. At least from outside. What's inside is a different thing. + + Programmers are not an exception. + + **It's always convenient when implementation details are hidden, and a simple, well-documented external interface is available.** + +To hide internal interface we use either protected or public properties: + +- Protected fields start with `_`. That's a well-known convention, not enforced at the language level. Programmers should only access a field starting with `_` from its class and classes inheriting from it. +- Private fields start with `#`. Javascript makes sure we only can access those from inside the class. + +Right now, private fields are not well-supported among browsers, but can be polyfilled. diff --git a/1-js/09-classes/05-private-protected-properties-methods/coffee-inside.jpg b/1-js/09-classes/05-private-protected-properties-methods/coffee-inside.jpg new file mode 100644 index 00000000..60f84664 Binary files /dev/null and b/1-js/09-classes/05-private-protected-properties-methods/coffee-inside.jpg differ diff --git a/1-js/09-classes/05-private-protected-properties-methods/coffee.jpg b/1-js/09-classes/05-private-protected-properties-methods/coffee.jpg new file mode 100644 index 00000000..ee26e1c0 Binary files /dev/null and b/1-js/09-classes/05-private-protected-properties-methods/coffee.jpg differ diff --git a/1-js/09-classes/06-extend-natives/article.md b/1-js/09-classes/06-extend-natives/article.md new file mode 100644 index 00000000..24757abe --- /dev/null +++ b/1-js/09-classes/06-extend-natives/article.md @@ -0,0 +1,82 @@ + +# Extending build-in classes + +Built-in classes like Array, Map and others are extendable also. + +For instance, here `PowerArray` inherits from the native `Array`: + +```js run +// add one more method to it (can do more) +class PowerArray extends Array { + isEmpty() { + return this.length === 0; + } +} + +let arr = new PowerArray(1, 2, 5, 10, 50); +alert(arr.isEmpty()); // false + +let filteredArr = arr.filter(item => item >= 10); +alert(filteredArr); // 10, 50 +alert(filteredArr.isEmpty()); // false +``` + +Please note a very interesting thing. Built-in methods like `filter`, `map` and others -- return new objects of exactly the inherited type. They rely on the `constructor` property to do so. + +In the example above, +```js +arr.constructor === PowerArray +``` + +So when `arr.filter()` is called, it internally creates the new array of results exactly as `new PowerArray`. +That's actually very cool, because we can keep using `PowerArray` methods further o the result. + +Even more, we can customize that behavior. + +There's a special static getter `Symbol.species`, if exists, it returns the constructor to use in such cases. + +If we'd like built-in methods like `map`, `filter` will return regular arrays, we can return `Array` in `Symbol.species`, like here: + +```js run +class PowerArray extends Array { + isEmpty() { + return this.length === 0; + } + +*!* + // built-in methods will use this as the constructor + static get [Symbol.species]() { + return Array; + } +*/!* +} + +let arr = new PowerArray(1, 2, 5, 10, 50); +alert(arr.isEmpty()); // false + +// filter creates new array using arr.constructor[Symbol.species] as constructor +let filteredArr = arr.filter(item => item >= 10); + +*!* +// filteredArr is not PowerArray, but Array +*/!* +alert(filteredArr.isEmpty()); // Error: filteredArr.isEmpty is not a function +``` + +As you can see, now `.filter` returns `Array`. So the extended functionality is not passed any further. + +## No static inheritance in built-ins + +Built-in objects have their own static methods, for instance `Object.keys`, `Array.isArray` etc. + +And we've already been talking about native classes extending each other: `Array.[[Prototype]] = Object`. + +But statics are an exception. Built-in classes don't inherit static properties from each other. + +In other words, the prototype of build-in constructor `Array` does not point to `Object`. This way `Array` and `Date` do not have `Array.keys` or `Date.keys`. And that feels natural. + +Here's the picture structure for `Date` and `Object`: + +![](object-date-inheritance.png) + +Note, there's no link between `Date` and `Object`. Both `Object` and `Date` exist independently. `Date.prototype` inherits from `Object.prototype`, but that's all. diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/object-date-inheritance.png b/1-js/09-classes/06-extend-natives/object-date-inheritance.png similarity index 66% rename from 1-js/07-object-oriented-programming/10-class-inheritance/object-date-inheritance.png rename to 1-js/09-classes/06-extend-natives/object-date-inheritance.png index adfb1cb9..b5f1932c 100644 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/object-date-inheritance.png and b/1-js/09-classes/06-extend-natives/object-date-inheritance.png differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/object-date-inheritance@2x.png b/1-js/09-classes/06-extend-natives/object-date-inheritance@2x.png similarity index 85% rename from 1-js/07-object-oriented-programming/10-class-inheritance/object-date-inheritance@2x.png rename to 1-js/09-classes/06-extend-natives/object-date-inheritance@2x.png index cfd85167..38276d45 100644 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/object-date-inheritance@2x.png and b/1-js/09-classes/06-extend-natives/object-date-inheritance@2x.png differ diff --git a/1-js/07-object-oriented-programming/11-instanceof/1-strange-instanceof/solution.md b/1-js/09-classes/07-instanceof/1-strange-instanceof/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/11-instanceof/1-strange-instanceof/solution.md rename to 1-js/09-classes/07-instanceof/1-strange-instanceof/solution.md diff --git a/1-js/07-object-oriented-programming/11-instanceof/1-strange-instanceof/task.md b/1-js/09-classes/07-instanceof/1-strange-instanceof/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/11-instanceof/1-strange-instanceof/task.md rename to 1-js/09-classes/07-instanceof/1-strange-instanceof/task.md diff --git a/1-js/07-object-oriented-programming/11-instanceof/article.md b/1-js/09-classes/07-instanceof/article.md similarity index 100% rename from 1-js/07-object-oriented-programming/11-instanceof/article.md rename to 1-js/09-classes/07-instanceof/article.md diff --git a/1-js/07-object-oriented-programming/11-instanceof/instanceof.png b/1-js/09-classes/07-instanceof/instanceof.png similarity index 100% rename from 1-js/07-object-oriented-programming/11-instanceof/instanceof.png rename to 1-js/09-classes/07-instanceof/instanceof.png diff --git a/1-js/07-object-oriented-programming/11-instanceof/instanceof@2x.png b/1-js/09-classes/07-instanceof/instanceof@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/11-instanceof/instanceof@2x.png rename to 1-js/09-classes/07-instanceof/instanceof@2x.png diff --git a/1-js/07-object-oriented-programming/13-mixins/article.md b/1-js/09-classes/08-mixins/article.md similarity index 100% rename from 1-js/07-object-oriented-programming/13-mixins/article.md rename to 1-js/09-classes/08-mixins/article.md diff --git a/1-js/07-object-oriented-programming/13-mixins/head.html b/1-js/09-classes/08-mixins/head.html similarity index 100% rename from 1-js/07-object-oriented-programming/13-mixins/head.html rename to 1-js/09-classes/08-mixins/head.html diff --git a/1-js/07-object-oriented-programming/13-mixins/mixin-inheritance.png b/1-js/09-classes/08-mixins/mixin-inheritance.png similarity index 100% rename from 1-js/07-object-oriented-programming/13-mixins/mixin-inheritance.png rename to 1-js/09-classes/08-mixins/mixin-inheritance.png diff --git a/1-js/07-object-oriented-programming/13-mixins/mixin-inheritance@2x.png b/1-js/09-classes/08-mixins/mixin-inheritance@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/13-mixins/mixin-inheritance@2x.png rename to 1-js/09-classes/08-mixins/mixin-inheritance@2x.png diff --git a/1-js/09-classes/index.md b/1-js/09-classes/index.md new file mode 100644 index 00000000..87846ef6 --- /dev/null +++ b/1-js/09-classes/index.md @@ -0,0 +1 @@ +# Classes diff --git a/1-js/08-error-handling/1-try-catch/1-finally-or-code-after/solution.md b/1-js/10-error-handling/1-try-catch/1-finally-or-code-after/solution.md similarity index 100% rename from 1-js/08-error-handling/1-try-catch/1-finally-or-code-after/solution.md rename to 1-js/10-error-handling/1-try-catch/1-finally-or-code-after/solution.md diff --git a/1-js/08-error-handling/1-try-catch/1-finally-or-code-after/task.md b/1-js/10-error-handling/1-try-catch/1-finally-or-code-after/task.md similarity index 100% rename from 1-js/08-error-handling/1-try-catch/1-finally-or-code-after/task.md rename to 1-js/10-error-handling/1-try-catch/1-finally-or-code-after/task.md diff --git a/1-js/08-error-handling/1-try-catch/article.md b/1-js/10-error-handling/1-try-catch/article.md similarity index 98% rename from 1-js/08-error-handling/1-try-catch/article.md rename to 1-js/10-error-handling/1-try-catch/article.md index ac3e0031..dacc2376 100644 --- a/1-js/08-error-handling/1-try-catch/article.md +++ b/1-js/10-error-handling/1-try-catch/article.md @@ -15,7 +15,7 @@ try { // code... -} catch (err) { +} catch (err)] { // error handling @@ -115,7 +115,7 @@ To catch an exception inside a scheduled function, `try..catch` must be inside t setTimeout(function() { try { noSuchVariable; // try..catch handles the error! - } catch (e) { + } catch { alert( "error is caught here!" ); } }, 1000); @@ -165,6 +165,19 @@ try { } ``` +## Optional "catch" binding + +[recent browser=new] + +If we don't need error details, `catch` may omit it: + +```js +try { + // ... +} catch { + // error object omitted +} +``` ## Using "try..catch" @@ -653,6 +666,8 @@ Error objects have following properties: - `name` -- the string with error name (error constructor name). - `stack` (non-standard) -- the stack at the moment of error creation. +If error is not needed, we can omit it by using `catch {` instead of `catch(err) {`. + We can also generate our own errors using the `throw` operator. Technically, the argument of `throw` can be anything, but usually it's an error object inheriting from the built-in `Error` class. More on extending errors in the next chapter. Rethrowing is a basic pattern of error handling: a `catch` block usually expects and knows how to handle the particular error type, so it should rethrow errors it doesn't know. diff --git a/1-js/08-error-handling/1-try-catch/try-catch-flow.png b/1-js/10-error-handling/1-try-catch/try-catch-flow.png similarity index 100% rename from 1-js/08-error-handling/1-try-catch/try-catch-flow.png rename to 1-js/10-error-handling/1-try-catch/try-catch-flow.png diff --git a/1-js/08-error-handling/1-try-catch/try-catch-flow@2x.png b/1-js/10-error-handling/1-try-catch/try-catch-flow@2x.png similarity index 100% rename from 1-js/08-error-handling/1-try-catch/try-catch-flow@2x.png rename to 1-js/10-error-handling/1-try-catch/try-catch-flow@2x.png diff --git a/1-js/08-error-handling/2-custom-errors/1-format-error/solution.md b/1-js/10-error-handling/2-custom-errors/1-format-error/solution.md similarity index 100% rename from 1-js/08-error-handling/2-custom-errors/1-format-error/solution.md rename to 1-js/10-error-handling/2-custom-errors/1-format-error/solution.md diff --git a/1-js/08-error-handling/2-custom-errors/1-format-error/task.md b/1-js/10-error-handling/2-custom-errors/1-format-error/task.md similarity index 100% rename from 1-js/08-error-handling/2-custom-errors/1-format-error/task.md rename to 1-js/10-error-handling/2-custom-errors/1-format-error/task.md diff --git a/1-js/08-error-handling/2-custom-errors/article.md b/1-js/10-error-handling/2-custom-errors/article.md similarity index 100% rename from 1-js/08-error-handling/2-custom-errors/article.md rename to 1-js/10-error-handling/2-custom-errors/article.md diff --git a/1-js/08-error-handling/index.md b/1-js/10-error-handling/index.md similarity index 100% rename from 1-js/08-error-handling/index.md rename to 1-js/10-error-handling/index.md diff --git a/1-js/10-modules/03-modules-dynamic-imports/article.md b/1-js/10-modules/03-modules-dynamic-imports/article.md deleted file mode 100644 index c7ea6290..00000000 --- a/1-js/10-modules/03-modules-dynamic-imports/article.md +++ /dev/null @@ -1,48 +0,0 @@ - -# Dynamic imports - -Export and import statements that we covered in previous chaters are called "static". - -What's because they are indeed static. - -First, we can't generate module name. It must be a string. This won't work: - -```js -import {func} from getModuleName(); // Error, only from "string" is allowed -``` - -Second, we can't import conditionally or at run-time: - -```js -if(...) { - import ...; // Error, not allowed! -} - -{ - import ...; // Error, we can't put import in a block, must be at the top level -} -``` - -So, import/export provide a rigid code structure. That's a good thing, as code structure can be analyzed, modules can be gathered and bundled together, unused exports can be removed (tree-shaken), just because everything is fixed. - -But how do we import a module on-demand? - -## The import() function - -The `import(module)` function can be called from anywhere. It returns a promise that resolved into a module object. - -The usage pattern looks like this: - -```js run -let modulePath = prompt("Module path?"); - -import(modulePath) - .then(obj => ) - .catch(err => ) -``` - -Or, we could use `async/away` here, like this: - -[codetabs src="say" current="index.html"] - -Please note: dynamic imports work in regular scripts, they don't require `script type="module"`, like static imports. diff --git a/1-js/09-async/01-callbacks/01-animate-circle-callback/solution.md b/1-js/11-async/01-callbacks/01-animate-circle-callback/solution.md similarity index 100% rename from 1-js/09-async/01-callbacks/01-animate-circle-callback/solution.md rename to 1-js/11-async/01-callbacks/01-animate-circle-callback/solution.md diff --git a/1-js/09-async/01-callbacks/01-animate-circle-callback/solution.view/index.html b/1-js/11-async/01-callbacks/01-animate-circle-callback/solution.view/index.html similarity index 100% rename from 1-js/09-async/01-callbacks/01-animate-circle-callback/solution.view/index.html rename to 1-js/11-async/01-callbacks/01-animate-circle-callback/solution.view/index.html diff --git a/1-js/09-async/01-callbacks/01-animate-circle-callback/task.md b/1-js/11-async/01-callbacks/01-animate-circle-callback/task.md similarity index 100% rename from 1-js/09-async/01-callbacks/01-animate-circle-callback/task.md rename to 1-js/11-async/01-callbacks/01-animate-circle-callback/task.md diff --git a/1-js/09-async/01-callbacks/article.md b/1-js/11-async/01-callbacks/article.md similarity index 100% rename from 1-js/09-async/01-callbacks/article.md rename to 1-js/11-async/01-callbacks/article.md diff --git a/1-js/09-async/01-callbacks/callback-hell.png b/1-js/11-async/01-callbacks/callback-hell.png similarity index 100% rename from 1-js/09-async/01-callbacks/callback-hell.png rename to 1-js/11-async/01-callbacks/callback-hell.png diff --git a/1-js/09-async/01-callbacks/callback-hell@2x.png b/1-js/11-async/01-callbacks/callback-hell@2x.png similarity index 100% rename from 1-js/09-async/01-callbacks/callback-hell@2x.png rename to 1-js/11-async/01-callbacks/callback-hell@2x.png diff --git a/1-js/09-async/01-callbacks/one.js b/1-js/11-async/01-callbacks/one.js similarity index 100% rename from 1-js/09-async/01-callbacks/one.js rename to 1-js/11-async/01-callbacks/one.js diff --git a/1-js/09-async/02-promise-basics/01-re-resolve/solution.md b/1-js/11-async/02-promise-basics/01-re-resolve/solution.md similarity index 100% rename from 1-js/09-async/02-promise-basics/01-re-resolve/solution.md rename to 1-js/11-async/02-promise-basics/01-re-resolve/solution.md diff --git a/1-js/09-async/02-promise-basics/01-re-resolve/task.md b/1-js/11-async/02-promise-basics/01-re-resolve/task.md similarity index 100% rename from 1-js/09-async/02-promise-basics/01-re-resolve/task.md rename to 1-js/11-async/02-promise-basics/01-re-resolve/task.md diff --git a/1-js/09-async/02-promise-basics/02-delay-promise/solution.md b/1-js/11-async/02-promise-basics/02-delay-promise/solution.md similarity index 100% rename from 1-js/09-async/02-promise-basics/02-delay-promise/solution.md rename to 1-js/11-async/02-promise-basics/02-delay-promise/solution.md diff --git a/1-js/09-async/02-promise-basics/02-delay-promise/task.md b/1-js/11-async/02-promise-basics/02-delay-promise/task.md similarity index 100% rename from 1-js/09-async/02-promise-basics/02-delay-promise/task.md rename to 1-js/11-async/02-promise-basics/02-delay-promise/task.md diff --git a/1-js/09-async/02-promise-basics/03-animate-circle-promise/solution.md b/1-js/11-async/02-promise-basics/03-animate-circle-promise/solution.md similarity index 100% rename from 1-js/09-async/02-promise-basics/03-animate-circle-promise/solution.md rename to 1-js/11-async/02-promise-basics/03-animate-circle-promise/solution.md diff --git a/1-js/09-async/02-promise-basics/03-animate-circle-promise/solution.view/index.html b/1-js/11-async/02-promise-basics/03-animate-circle-promise/solution.view/index.html similarity index 100% rename from 1-js/09-async/02-promise-basics/03-animate-circle-promise/solution.view/index.html rename to 1-js/11-async/02-promise-basics/03-animate-circle-promise/solution.view/index.html diff --git a/1-js/09-async/02-promise-basics/03-animate-circle-promise/task.md b/1-js/11-async/02-promise-basics/03-animate-circle-promise/task.md similarity index 100% rename from 1-js/09-async/02-promise-basics/03-animate-circle-promise/task.md rename to 1-js/11-async/02-promise-basics/03-animate-circle-promise/task.md diff --git a/1-js/09-async/02-promise-basics/article.md b/1-js/11-async/02-promise-basics/article.md similarity index 100% rename from 1-js/09-async/02-promise-basics/article.md rename to 1-js/11-async/02-promise-basics/article.md diff --git a/1-js/09-async/02-promise-basics/head.html b/1-js/11-async/02-promise-basics/head.html similarity index 100% rename from 1-js/09-async/02-promise-basics/head.html rename to 1-js/11-async/02-promise-basics/head.html diff --git a/1-js/09-async/02-promise-basics/promise-init.png b/1-js/11-async/02-promise-basics/promise-init.png similarity index 100% rename from 1-js/09-async/02-promise-basics/promise-init.png rename to 1-js/11-async/02-promise-basics/promise-init.png diff --git a/1-js/09-async/02-promise-basics/promise-init@2x.png b/1-js/11-async/02-promise-basics/promise-init@2x.png similarity index 100% rename from 1-js/09-async/02-promise-basics/promise-init@2x.png rename to 1-js/11-async/02-promise-basics/promise-init@2x.png diff --git a/1-js/09-async/02-promise-basics/promise-reject-1.png b/1-js/11-async/02-promise-basics/promise-reject-1.png similarity index 100% rename from 1-js/09-async/02-promise-basics/promise-reject-1.png rename to 1-js/11-async/02-promise-basics/promise-reject-1.png diff --git a/1-js/09-async/02-promise-basics/promise-reject-1@2x.png b/1-js/11-async/02-promise-basics/promise-reject-1@2x.png similarity index 100% rename from 1-js/09-async/02-promise-basics/promise-reject-1@2x.png rename to 1-js/11-async/02-promise-basics/promise-reject-1@2x.png diff --git a/1-js/09-async/02-promise-basics/promise-resolve-1.png b/1-js/11-async/02-promise-basics/promise-resolve-1.png similarity index 100% rename from 1-js/09-async/02-promise-basics/promise-resolve-1.png rename to 1-js/11-async/02-promise-basics/promise-resolve-1.png diff --git a/1-js/09-async/02-promise-basics/promise-resolve-1@2x.png b/1-js/11-async/02-promise-basics/promise-resolve-1@2x.png similarity index 100% rename from 1-js/09-async/02-promise-basics/promise-resolve-1@2x.png rename to 1-js/11-async/02-promise-basics/promise-resolve-1@2x.png diff --git a/1-js/09-async/02-promise-basics/promise-resolve-reject.png b/1-js/11-async/02-promise-basics/promise-resolve-reject.png similarity index 100% rename from 1-js/09-async/02-promise-basics/promise-resolve-reject.png rename to 1-js/11-async/02-promise-basics/promise-resolve-reject.png diff --git a/1-js/09-async/02-promise-basics/promise-resolve-reject@2x.png b/1-js/11-async/02-promise-basics/promise-resolve-reject@2x.png similarity index 100% rename from 1-js/09-async/02-promise-basics/promise-resolve-reject@2x.png rename to 1-js/11-async/02-promise-basics/promise-resolve-reject@2x.png diff --git a/1-js/09-async/03-promise-chaining/01-then-vs-catch/solution.md b/1-js/11-async/03-promise-chaining/01-then-vs-catch/solution.md similarity index 100% rename from 1-js/09-async/03-promise-chaining/01-then-vs-catch/solution.md rename to 1-js/11-async/03-promise-chaining/01-then-vs-catch/solution.md diff --git a/1-js/09-async/03-promise-chaining/01-then-vs-catch/task.md b/1-js/11-async/03-promise-chaining/01-then-vs-catch/task.md similarity index 100% rename from 1-js/09-async/03-promise-chaining/01-then-vs-catch/task.md rename to 1-js/11-async/03-promise-chaining/01-then-vs-catch/task.md diff --git a/1-js/09-async/03-promise-chaining/article.md b/1-js/11-async/03-promise-chaining/article.md similarity index 100% rename from 1-js/09-async/03-promise-chaining/article.md rename to 1-js/11-async/03-promise-chaining/article.md diff --git a/1-js/09-async/03-promise-chaining/getMessage.js b/1-js/11-async/03-promise-chaining/getMessage.js similarity index 100% rename from 1-js/09-async/03-promise-chaining/getMessage.js rename to 1-js/11-async/03-promise-chaining/getMessage.js diff --git a/1-js/09-async/03-promise-chaining/head.html b/1-js/11-async/03-promise-chaining/head.html similarity index 100% rename from 1-js/09-async/03-promise-chaining/head.html rename to 1-js/11-async/03-promise-chaining/head.html diff --git a/1-js/09-async/03-promise-chaining/one.js b/1-js/11-async/03-promise-chaining/one.js similarity index 100% rename from 1-js/09-async/03-promise-chaining/one.js rename to 1-js/11-async/03-promise-chaining/one.js diff --git a/1-js/09-async/03-promise-chaining/promise-handler-variants-2.png b/1-js/11-async/03-promise-chaining/promise-handler-variants-2.png similarity index 100% rename from 1-js/09-async/03-promise-chaining/promise-handler-variants-2.png rename to 1-js/11-async/03-promise-chaining/promise-handler-variants-2.png diff --git a/1-js/09-async/03-promise-chaining/promise-handler-variants-2@2x.png b/1-js/11-async/03-promise-chaining/promise-handler-variants-2@2x.png similarity index 100% rename from 1-js/09-async/03-promise-chaining/promise-handler-variants-2@2x.png rename to 1-js/11-async/03-promise-chaining/promise-handler-variants-2@2x.png diff --git a/1-js/09-async/03-promise-chaining/promise-handler-variants.png b/1-js/11-async/03-promise-chaining/promise-handler-variants.png similarity index 100% rename from 1-js/09-async/03-promise-chaining/promise-handler-variants.png rename to 1-js/11-async/03-promise-chaining/promise-handler-variants.png diff --git a/1-js/09-async/03-promise-chaining/promise-handler-variants@2x.png b/1-js/11-async/03-promise-chaining/promise-handler-variants@2x.png similarity index 100% rename from 1-js/09-async/03-promise-chaining/promise-handler-variants@2x.png rename to 1-js/11-async/03-promise-chaining/promise-handler-variants@2x.png diff --git a/1-js/09-async/03-promise-chaining/promise-then-chain.png b/1-js/11-async/03-promise-chaining/promise-then-chain.png similarity index 100% rename from 1-js/09-async/03-promise-chaining/promise-then-chain.png rename to 1-js/11-async/03-promise-chaining/promise-then-chain.png diff --git a/1-js/09-async/03-promise-chaining/promise-then-chain@2x.png b/1-js/11-async/03-promise-chaining/promise-then-chain@2x.png similarity index 100% rename from 1-js/09-async/03-promise-chaining/promise-then-chain@2x.png rename to 1-js/11-async/03-promise-chaining/promise-then-chain@2x.png diff --git a/1-js/09-async/03-promise-chaining/promise-then-many.png b/1-js/11-async/03-promise-chaining/promise-then-many.png similarity index 100% rename from 1-js/09-async/03-promise-chaining/promise-then-many.png rename to 1-js/11-async/03-promise-chaining/promise-then-many.png diff --git a/1-js/09-async/03-promise-chaining/promise-then-many@2x.png b/1-js/11-async/03-promise-chaining/promise-then-many@2x.png similarity index 100% rename from 1-js/09-async/03-promise-chaining/promise-then-many@2x.png rename to 1-js/11-async/03-promise-chaining/promise-then-many@2x.png diff --git a/1-js/09-async/03-promise-chaining/three.js b/1-js/11-async/03-promise-chaining/three.js similarity index 100% rename from 1-js/09-async/03-promise-chaining/three.js rename to 1-js/11-async/03-promise-chaining/three.js diff --git a/1-js/09-async/03-promise-chaining/two.js b/1-js/11-async/03-promise-chaining/two.js similarity index 100% rename from 1-js/09-async/03-promise-chaining/two.js rename to 1-js/11-async/03-promise-chaining/two.js diff --git a/1-js/09-async/03-promise-chaining/user.json b/1-js/11-async/03-promise-chaining/user.json similarity index 100% rename from 1-js/09-async/03-promise-chaining/user.json rename to 1-js/11-async/03-promise-chaining/user.json diff --git a/1-js/09-async/04-promise-error-handling/01-error-async/solution.md b/1-js/11-async/04-promise-error-handling/01-error-async/solution.md similarity index 100% rename from 1-js/09-async/04-promise-error-handling/01-error-async/solution.md rename to 1-js/11-async/04-promise-error-handling/01-error-async/solution.md diff --git a/1-js/09-async/04-promise-error-handling/01-error-async/task.md b/1-js/11-async/04-promise-error-handling/01-error-async/task.md similarity index 100% rename from 1-js/09-async/04-promise-error-handling/01-error-async/task.md rename to 1-js/11-async/04-promise-error-handling/01-error-async/task.md diff --git a/1-js/09-async/04-promise-error-handling/article.md b/1-js/11-async/04-promise-error-handling/article.md similarity index 100% rename from 1-js/09-async/04-promise-error-handling/article.md rename to 1-js/11-async/04-promise-error-handling/article.md diff --git a/1-js/09-async/04-promise-error-handling/getMessage.js b/1-js/11-async/04-promise-error-handling/getMessage.js similarity index 100% rename from 1-js/09-async/04-promise-error-handling/getMessage.js rename to 1-js/11-async/04-promise-error-handling/getMessage.js diff --git a/1-js/09-async/04-promise-error-handling/head.html b/1-js/11-async/04-promise-error-handling/head.html similarity index 100% rename from 1-js/09-async/04-promise-error-handling/head.html rename to 1-js/11-async/04-promise-error-handling/head.html diff --git a/1-js/09-async/04-promise-error-handling/one.js b/1-js/11-async/04-promise-error-handling/one.js similarity index 100% rename from 1-js/09-async/04-promise-error-handling/one.js rename to 1-js/11-async/04-promise-error-handling/one.js diff --git a/1-js/09-async/04-promise-error-handling/promise-handler-variants-2.png b/1-js/11-async/04-promise-error-handling/promise-handler-variants-2.png similarity index 100% rename from 1-js/09-async/04-promise-error-handling/promise-handler-variants-2.png rename to 1-js/11-async/04-promise-error-handling/promise-handler-variants-2.png diff --git a/1-js/09-async/04-promise-error-handling/promise-handler-variants-2@2x.png b/1-js/11-async/04-promise-error-handling/promise-handler-variants-2@2x.png similarity index 100% rename from 1-js/09-async/04-promise-error-handling/promise-handler-variants-2@2x.png rename to 1-js/11-async/04-promise-error-handling/promise-handler-variants-2@2x.png diff --git a/1-js/09-async/04-promise-error-handling/promise-handler-variants.png b/1-js/11-async/04-promise-error-handling/promise-handler-variants.png similarity index 100% rename from 1-js/09-async/04-promise-error-handling/promise-handler-variants.png rename to 1-js/11-async/04-promise-error-handling/promise-handler-variants.png diff --git a/1-js/09-async/04-promise-error-handling/promise-handler-variants@2x.png b/1-js/11-async/04-promise-error-handling/promise-handler-variants@2x.png similarity index 100% rename from 1-js/09-async/04-promise-error-handling/promise-handler-variants@2x.png rename to 1-js/11-async/04-promise-error-handling/promise-handler-variants@2x.png diff --git a/1-js/09-async/04-promise-error-handling/promise-then-chain.png b/1-js/11-async/04-promise-error-handling/promise-then-chain.png similarity index 100% rename from 1-js/09-async/04-promise-error-handling/promise-then-chain.png rename to 1-js/11-async/04-promise-error-handling/promise-then-chain.png diff --git a/1-js/09-async/04-promise-error-handling/promise-then-chain@2x.png b/1-js/11-async/04-promise-error-handling/promise-then-chain@2x.png similarity index 100% rename from 1-js/09-async/04-promise-error-handling/promise-then-chain@2x.png rename to 1-js/11-async/04-promise-error-handling/promise-then-chain@2x.png diff --git a/1-js/09-async/04-promise-error-handling/promise-then-many.png b/1-js/11-async/04-promise-error-handling/promise-then-many.png similarity index 100% rename from 1-js/09-async/04-promise-error-handling/promise-then-many.png rename to 1-js/11-async/04-promise-error-handling/promise-then-many.png diff --git a/1-js/09-async/04-promise-error-handling/promise-then-many@2x.png b/1-js/11-async/04-promise-error-handling/promise-then-many@2x.png similarity index 100% rename from 1-js/09-async/04-promise-error-handling/promise-then-many@2x.png rename to 1-js/11-async/04-promise-error-handling/promise-then-many@2x.png diff --git a/1-js/09-async/04-promise-error-handling/three.js b/1-js/11-async/04-promise-error-handling/three.js similarity index 100% rename from 1-js/09-async/04-promise-error-handling/three.js rename to 1-js/11-async/04-promise-error-handling/three.js diff --git a/1-js/09-async/04-promise-error-handling/two.js b/1-js/11-async/04-promise-error-handling/two.js similarity index 100% rename from 1-js/09-async/04-promise-error-handling/two.js rename to 1-js/11-async/04-promise-error-handling/two.js diff --git a/1-js/09-async/04-promise-error-handling/user.json b/1-js/11-async/04-promise-error-handling/user.json similarity index 100% rename from 1-js/09-async/04-promise-error-handling/user.json rename to 1-js/11-async/04-promise-error-handling/user.json diff --git a/1-js/09-async/05-promise-api/01-promise-errors-as-results/solution.md b/1-js/11-async/05-promise-api/01-promise-errors-as-results/solution.md similarity index 100% rename from 1-js/09-async/05-promise-api/01-promise-errors-as-results/solution.md rename to 1-js/11-async/05-promise-api/01-promise-errors-as-results/solution.md diff --git a/1-js/09-async/05-promise-api/01-promise-errors-as-results/solution.view/index.html b/1-js/11-async/05-promise-api/01-promise-errors-as-results/solution.view/index.html similarity index 100% rename from 1-js/09-async/05-promise-api/01-promise-errors-as-results/solution.view/index.html rename to 1-js/11-async/05-promise-api/01-promise-errors-as-results/solution.view/index.html diff --git a/1-js/09-async/05-promise-api/01-promise-errors-as-results/source.view/index.html b/1-js/11-async/05-promise-api/01-promise-errors-as-results/source.view/index.html similarity index 100% rename from 1-js/09-async/05-promise-api/01-promise-errors-as-results/source.view/index.html rename to 1-js/11-async/05-promise-api/01-promise-errors-as-results/source.view/index.html diff --git a/1-js/09-async/05-promise-api/01-promise-errors-as-results/task.md b/1-js/11-async/05-promise-api/01-promise-errors-as-results/task.md similarity index 100% rename from 1-js/09-async/05-promise-api/01-promise-errors-as-results/task.md rename to 1-js/11-async/05-promise-api/01-promise-errors-as-results/task.md diff --git a/1-js/09-async/05-promise-api/02-promise-errors-as-results-2/solution.md b/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/solution.md similarity index 100% rename from 1-js/09-async/05-promise-api/02-promise-errors-as-results-2/solution.md rename to 1-js/11-async/05-promise-api/02-promise-errors-as-results-2/solution.md diff --git a/1-js/09-async/05-promise-api/02-promise-errors-as-results-2/solution.view/index.html b/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/solution.view/index.html similarity index 100% rename from 1-js/09-async/05-promise-api/02-promise-errors-as-results-2/solution.view/index.html rename to 1-js/11-async/05-promise-api/02-promise-errors-as-results-2/solution.view/index.html diff --git a/1-js/09-async/05-promise-api/02-promise-errors-as-results-2/source.view/index.html b/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/source.view/index.html similarity index 100% rename from 1-js/09-async/05-promise-api/02-promise-errors-as-results-2/source.view/index.html rename to 1-js/11-async/05-promise-api/02-promise-errors-as-results-2/source.view/index.html diff --git a/1-js/09-async/05-promise-api/02-promise-errors-as-results-2/task.md b/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/task.md similarity index 100% rename from 1-js/09-async/05-promise-api/02-promise-errors-as-results-2/task.md rename to 1-js/11-async/05-promise-api/02-promise-errors-as-results-2/task.md diff --git a/1-js/09-async/05-promise-api/article.md b/1-js/11-async/05-promise-api/article.md similarity index 100% rename from 1-js/09-async/05-promise-api/article.md rename to 1-js/11-async/05-promise-api/article.md diff --git a/1-js/09-async/05-promise-api/head.html b/1-js/11-async/05-promise-api/head.html similarity index 100% rename from 1-js/09-async/05-promise-api/head.html rename to 1-js/11-async/05-promise-api/head.html diff --git a/1-js/09-async/05-promise-api/iliakan.json b/1-js/11-async/05-promise-api/iliakan.json similarity index 100% rename from 1-js/09-async/05-promise-api/iliakan.json rename to 1-js/11-async/05-promise-api/iliakan.json diff --git a/1-js/09-async/05-promise-api/one.js b/1-js/11-async/05-promise-api/one.js similarity index 100% rename from 1-js/09-async/05-promise-api/one.js rename to 1-js/11-async/05-promise-api/one.js diff --git a/1-js/09-async/05-promise-api/two.js b/1-js/11-async/05-promise-api/two.js similarity index 100% rename from 1-js/09-async/05-promise-api/two.js rename to 1-js/11-async/05-promise-api/two.js diff --git a/1-js/09-async/06-async-await/01-rewrite-async-2/solution.md b/1-js/11-async/06-async-await/01-rewrite-async-2/solution.md similarity index 100% rename from 1-js/09-async/06-async-await/01-rewrite-async-2/solution.md rename to 1-js/11-async/06-async-await/01-rewrite-async-2/solution.md diff --git a/1-js/09-async/06-async-await/01-rewrite-async-2/task.md b/1-js/11-async/06-async-await/01-rewrite-async-2/task.md similarity index 100% rename from 1-js/09-async/06-async-await/01-rewrite-async-2/task.md rename to 1-js/11-async/06-async-await/01-rewrite-async-2/task.md diff --git a/1-js/09-async/06-async-await/01-rewrite-async/solution.md b/1-js/11-async/06-async-await/01-rewrite-async/solution.md similarity index 100% rename from 1-js/09-async/06-async-await/01-rewrite-async/solution.md rename to 1-js/11-async/06-async-await/01-rewrite-async/solution.md diff --git a/1-js/09-async/06-async-await/01-rewrite-async/task.md b/1-js/11-async/06-async-await/01-rewrite-async/task.md similarity index 100% rename from 1-js/09-async/06-async-await/01-rewrite-async/task.md rename to 1-js/11-async/06-async-await/01-rewrite-async/task.md diff --git a/1-js/09-async/06-async-await/article.md b/1-js/11-async/06-async-await/article.md similarity index 100% rename from 1-js/09-async/06-async-await/article.md rename to 1-js/11-async/06-async-await/article.md diff --git a/1-js/09-async/06-async-await/head.html b/1-js/11-async/06-async-await/head.html similarity index 100% rename from 1-js/09-async/06-async-await/head.html rename to 1-js/11-async/06-async-await/head.html diff --git a/1-js/09-async/07-async-iteration-generators/article.md b/1-js/11-async/07-async-iteration-generators/article.md similarity index 59% rename from 1-js/09-async/07-async-iteration-generators/article.md rename to 1-js/11-async/07-async-iteration-generators/article.md index 8e490273..743f5706 100644 --- a/1-js/09-async/07-async-iteration-generators/article.md +++ b/1-js/11-async/07-async-iteration-generators/article.md @@ -3,23 +3,57 @@ Asynchronous iterators allow to iterate over data that comes asynchronously, on-demand. -For instance, when we download something chunk-by-chunk, or just expect a events to come asynchronously and would like to iterate over that -- async iterators and generators may come in handy. - -Let's see a simple example first, to grasp the syntax, and then build a real-life example. +For instance, when we download something chunk-by-chunk, or just expect events to come asynchronously and would like to iterate over them -- async iterators and generators may come in handy. Let's see a simple example first, to grasp the syntax, and then review a real-life use case. ## Async iterators Asynchronous iterators are totally similar to regular iterators, with a few syntactic differences. -Here's a small cheatsheet: +"Regular" iterable object from the chapter look like this: -| | Iterators | Async iterators | -|-------|-----------|-----------------| -| Object method to provide iteraterable | `Symbol.iterator` | `Symbol.asyncIterator` | -| `next()` return value is | any value | `Promise` | -| to loop, use | `for..of` | `for await..of` | +```js run +let range = { + from: 1, + to: 5, -Let's make an iterable `range` object, like we did in the chapter [](info:iterable), but now it will return values asynchronously, one per second: + // for..of calls this method once in the very beginning +*!* + [Symbol.iterator]() { +*/!* + // ...it returns the iterator object: + // onward, for..of works only with that object, asking it for next values + return { + current: this.from, + last: this.to, + + // next() is called on each iteration by the for..of loop +*!* + next() { // (2) + // it should return the value as an object {done:.., value :...} +*/!* + if (this.current <= this.last) { + return { done: false, value: this.current++ }; + } else { + return { done: true }; + } + } + }; + } +}; + +for(let value in range) { + alert(value); // 1 then 2, then 3, then 4, then 5 +} +``` + +If necessary, please refer to the [chapter about iterables](info:iterable) for details about regular iterators. + +To make the object iterable asynchronously: +1. We need to use `Symbol.asyncIterator` instead of `Symbol.iterator`. +2. `next()` should return a promise. +3. To iterate over such an object, we should use `for await (let item of iterable)` loop. + +Let's make an iterable `range` object, like the one before, but now it will return values asynchronously, one per second: ```js run let range = { @@ -44,7 +78,7 @@ let range = { */!* // can use await inside, do async stuff: - await new Promise(resolve => setTimeout(resolve, 1000)); // (4) + await new Promise(resolve => setTimeout(resolve, 1000)); // (3) if (this.current <= this.last) { return { done: false, value: this.current++ }; @@ -59,7 +93,7 @@ let range = { (async () => { *!* - for await (let value of range) { // (3) + for await (let value of range) { // (4) alert(value); // 1,2,3,4,5 } */!* @@ -70,11 +104,20 @@ let range = { As we can see, the components are similar to regular iterators: 1. To make an object asynchronously iterable, it must have a method `Symbol.asyncIterator` `(1)`. -2. To iterate, we use `for await(let value of range)` `(3)`, namely add "await" after "for". -3. It calls `range[Symbol.asyncIterator]` and expects it to return an object with `next` `(3)` method returning a promise. -4. Then `next()` is called to obtain values. In that example values come with a delay of 1 second `(4)`. +2. It must return the object with `next()` method returning a promise `(2)`. +3. The `next()` method doesn't have to be `async`, it may be a regular method returning a promise, but `async` allows to use `await` inside. Here we just delay for a second `(3)`. +4. To iterate, we use `for await(let value of range)` `(4)`, namely add "await" after "for". It calls `range[Symbol.asyncIterator]()` once, and then its `next()` for values. -````warn header="A spread operator doesn't work asynchronously" +Here's a small cheatsheet: + +| | Iterators | Async iterators | +|-------|-----------|-----------------| +| Object method to provide iteraterable | `Symbol.iterator` | `Symbol.asyncIterator` | +| `next()` return value is | any value | `Promise` | +| to loop, use | `for..of` | `for await..of` | + + +````warn header="The spread operator doesn't work asynchronously" Features that require regular, synchronous iterators, don't work with asynchronous ones. For instance, a spread operator won't work: @@ -82,14 +125,14 @@ For instance, a spread operator won't work: alert( [...range] ); // Error, no Symbol.iterator ``` -That's natural, as it expects to find `Symbol.iterator`, and there's none. +That's natural, as it expects to find `Symbol.iterator`, same as `for..of` without `await`. ```` ## Async generators Javascript also provides generators, that are also iterable. -Let's recall a sequence generator from the chapter [](info:generators): +Let's recall a sequence generator from the chapter [](info:generators). It generates a sequence of values from `start` to `end` (could be anything else): ```js run function* generateSequence(start, end) { @@ -103,9 +146,12 @@ for(let value of generateSequence(1, 5)) { } ``` -Normally, we can't use `await` in generators. But what if we need to? -No problem, let's make an async generator, like this: +Normally, we can't use `await` in generators. All values must come synchronously: there's no place for delay in `for..of`. + +But what if we need to use `await` in the generator body? To perform network requests, for instance. + +No problem, just prepend it with `async`, like this: ```js run *!*async*/!* function* generateSequence(start, end) { @@ -132,9 +178,11 @@ No problem, let's make an async generator, like this: })(); ``` -So simple, a little bit magical. We add the `async` keyword, and the generator now can use `await` inside of it, rely on promises and other async functions. +Now we have an the async generator, iteratable with `for await...of`. -Technically, the difference of such generator is that `generator.next()` method is now asynchronous. +It's indeed very simple. We add the `async` keyword, and the generator now can use `await` inside of it, rely on promises and other async functions. + +Technically, another the difference of an async generator is that its `generator.next()` method is now asynchronous also, it returns promises. Instead of `result = generator.next()` for a regular, non-async generator, values can be obtained like this: @@ -144,7 +192,19 @@ result = await generator.next(); // result = {value: ..., done: true/false} ## Iterables via async generators -When we'd like to make an object iterable, we should add `Symbol.iterator` to it. A common practice is to implement it via a generator, just because that's simple. +When we'd like to make an object iterable, we should add `Symbol.iterator` to it. + +```js +let range = { + from: 1, + to: 5, +*!* + [Symbol.iterator]() { ...return object with next to make range iterable... } +*/!* +} +``` + +A common practice for `Symbol.iterator` is to return a generator, rather than a plain object with `next` as in the example before. Let's recall an example from the chapter [](info:generators): @@ -165,9 +225,9 @@ for(let value of range) { } ``` -Here a custom object `range` was made iterable, the generator `*[Symbol.iterator]` implements the logic behind listing values. +Here a custom object `range` is iterable, and the generator `*[Symbol.iterator]` implements the logic for listing values. -We can make it iterable asynchronously by replacing `Symbol.iterator` with `Symbol.asyncIterator` and marking it `async`: +If we'd like to add async actions into the generator, then we should replace `Symbol.iterator` with async `Symbol.asyncIterator`: ```js run let range = { @@ -196,17 +256,21 @@ let range = { })(); ``` +Now values come with a delay of 1 second between them. + ## Real-life example +So far we've seen simple examples, to gain basic understanding. Now let's review a real-life use case. + There are many online APIs that deliver paginated data. For instance, when we need a list of users, then we can fetch it page-by-page: a request returns a pre-defined count (e.g. 100 users), and provides an URL to the next page. The pattern is very common, it's not about users, but just about anything. For instance, Github allows to retrieve commits in the same, paginated fasion: -- We should make a request to URL in the form `https://api.github.com/repos/(repo)/commits`. +- We should make a request to URL in the form `https://api.github.com/repos//commits`. - It responds with a JSON of 30 commits, and also provides a link to the next page in the `Link` header. -- Then we can use it for the next request, if need more commits, and so on. +- Then we can use that link for the next request, to get more commits, and so on. -What we'd like to have is an iterable source of commits, so that we could do it like this: +What we'd like to have is an iterable source of commits, so that we could use it like this: ```js let repo = 'iliakan/javascript-tutorial-en'; // Github repository to get commits from @@ -216,7 +280,7 @@ for await (let commit of fetchCommits(repo)) { } ``` -We'd like `fetchCommits` to get commits for us, making requests whenever needed. And let it care about all pagination stuff. +We'd like `fetchCommits` to get commits for us, making requests whenever needed. And let it care about all pagination stuff, for us it'll be a simple `for await..of`. With async generators that's pretty easy to implement: @@ -244,9 +308,9 @@ async function* fetchCommits(repo) { } ``` -1. We use the browser `fetch` method to download from a remote URL. It allows to supply authorization and other headers if needed (Github requires User-Agent). -2. The result is parsed as JSON. -3. ...And we extract the next page URL from the `Link` header of the response. It has a special format, so we use a regexp for that. The next page URL may look like this: `https://api.github.com/repositories/93253246/commits?page=2`, it's generatd by Github itself. +1. We use the browser `fetch` method to download from a remote URL. It allows to supply authorization and other headers if needed, here Github requires `User-Agent`. +2. The fetch result is parsed as JSON, that's again a `fetch`-specific method. +3. We can get the next page URL from the `Link` header of the response. It has a special format, so we use a regexp for that. The next page URL may look like this: `https://api.github.com/repositories/93253246/commits?page=2`, it's generatd by Github itself. 4. Then we yield all commits received, and when they finish -- the next `while(url)` iteration will trigger, making one more request. An example of use (shows commit authors in console): @@ -268,7 +332,7 @@ An example of use (shows commit authors in console): })(); ``` -The internal mechanics is invisible from the outside. What we see -- is an async generator that returns commits, just what we need. +That's just what we wanted. The internal pagination mechanics is invisible from the outside. For us it's just an async generator that returns commits. ## Summary @@ -292,6 +356,6 @@ Syntax differences between async and regular generators: In web-development we often meet streams of data, when it flows chunk-by-chunk. For instance, downloading or uploading a big file. -We could use async generators to process such data, but there's also another API called Streams, that may be more convenient. It's not a part of Javascript language standard though. +We could use async generators to process such data, but there's also another API called Streams, that may be more convenient, as it provides special interfaces to transform the data and to pass it from one stream to another (e.g. download from one place and immediately send elsewhere). But they are also more complex. -Streams and async generators complement each other, both are great ways to handle async data flows. +Streams API not a part of Javascript language standard. Streams and async generators complement each other, both are great ways to handle async data flows. diff --git a/1-js/09-async/07-async-iteration-generators/head.html b/1-js/11-async/07-async-iteration-generators/head.html similarity index 100% rename from 1-js/09-async/07-async-iteration-generators/head.html rename to 1-js/11-async/07-async-iteration-generators/head.html diff --git a/1-js/09-async/index.md b/1-js/11-async/index.md similarity index 100% rename from 1-js/09-async/index.md rename to 1-js/11-async/index.md diff --git a/1-js/10-modules/01-modules-intro/article.md b/1-js/12-modules/01-modules-intro/article.md similarity index 66% rename from 1-js/10-modules/01-modules-intro/article.md rename to 1-js/12-modules/01-modules-intro/article.md index 749997e3..ad83d425 100644 --- a/1-js/10-modules/01-modules-intro/article.md +++ b/1-js/12-modules/01-modules-intro/article.md @@ -1,5 +1,5 @@ -# Modules, the introduction +# Modules, introduction As our application grows bigger, we want to split it into multiple files, so called 'modules'. A module usually contains a class or a library of useful functions. @@ -44,8 +44,6 @@ alert(sayHi); // function... sayHi('John'); // Hello, John! ``` -## Modules in the browser - In this tutorial we concentrate on the language itself, but we use browser as the demo environment, so let's see how modules work in the browser. To use modules, we must set the attribute ` - ``` - - That ensures better security by default. - -2. External scripts with same `src` run only once: - ```html - - - - ``` - -3. Module scripts are *always* deferred, same effect as `defer` attribute (described in the chapter [](info:onload-ondomcontentloaded)), for both external and inline scripts. - - In other words: - - external module scripts ` - - - - - ``` - - Please note: the second script actually works before the first! So we'll see `undefined` first, and then `object`. - - That's because modules are deferred, so way wait for the document to be processed. The regular scripts runs immediately, so we saw its output first. - - When using modules, we should be aware that HTML-document can show up before the Javascript application is ready. Some functionality may not work yet. We should put transparent overlays or "loading indicators", or otherwise ensure that the visitor won't be confused because of it. - -4. Async attribute ` - ``` - -Also, in the browser, in scripts (not in HTML), `import` must get either a relative or absolute URL. So-called "bare" modules (without path) are not allowed. - -For instance, this `import` is invalid: -```js -import {sayHi} from 'sayHi'; // Error, "bare" module -// must be './sayHi.js' or wherever the module is -``` - -Certain environments, like Node.js or bundle tools allow bare modules, as they have own ways for finding modules and hooks to fine-tune them. But browsers do not support bare modules yet. - -### Build tools - -In real-life, browser modules are rarely used in their "raw" form. Usually, we bundle them together with a special tool such as [Webpack](https://webpack.js.org/) and deploy to the production server. - -One of the benefits of using bundlers -- they give more control over how modules are resolved, allowing bare modules and much more, like CSS/HTML modules. - -Build tools do the following: - -1. Take a "main" module, the one intended to be put in ` - - -``` - -If we use bundle tools, then as modules are bundled together, their `import/export` statements are replaced by special bundler calls, so the resulting build does not require `type="module"`, and we can put it into a regular script: - -```html - - -``` - - ## Core module features -In the previous section we covered browser-specific features. Now it should be easy to start using modules in the browser. +What's different in modules, compared to "regular" scripts? -There are other module features, not bound to the host environment, valid both for browser and Node.js. +There are core features, valid both for browser and server-side Javascript. ### Always "use strict" @@ -199,9 +72,21 @@ Here we can see it in the browser, but the same is true for any module. ### Module-level scope -Each module has its own top-level scope. In other words, top-level variables and functions from a module are not seen in other scripts. Modules are expected to `export` what they want to be accessible from outside. +Each module has its own top-level scope. In other words, top-level variables and functions from a module are not seen in other scripts. -Independant top-level scope exists for ` ``` -If we really need to share the variable, we can explicitly assign it to `window.user`. But that's an exception requiring a good reason. +If we really need to make a "global" in-browser variable, we can explicitly assign it to `window` and access as `window.user`. But that's an exception requiring a good reason. ### A module code is evaluated only the first time when imported @@ -309,10 +194,12 @@ sayHi(); // Ready to serve, *!*Pete*/!*! The object `import.meta` contains the information about the current module. -Its content depends on the environment, in a browser it contains the url of the script, or a webpage if inside HTML +Its content depends on the environment. In the browser, it contains the url of the script, or a current webpage url if inside HTML: -``` -alert(import.meta.url); // the url of the current page (or the script URL) +```html run height=0 + ``` ### Top-level "this" is undefined @@ -331,18 +218,158 @@ In a module, top-level `this` is undefined, as opposed to a global object in non ``` +## Browser-specific features + +There are also several browser-specific differences of scripts with `type="module"` compared to regular ones. + +You may want skip those for now if you're reading for the first time, or if you don't use Javascript in a browser. + +### Module scripts are deferred + +Module scripts are *always* deferred, same effect as `defer` attribute (described in the chapter [](info:onload-ondomcontentloaded)), for both external and inline scripts. + +In other words: +- external module scripts ` + + + + +``` + +Please note: the second script actually works before the first! So we'll see `undefined` first, and then `object`. + +That's because modules are deferred, so way wait for the document to be processed. The regular scripts runs immediately, so we saw its output first. + +When using modules, we should be aware that HTML-document can show up before the Javascript application is ready. Some functionality may not work yet. We should put transparent overlays or "loading indicators", or otherwise ensure that the visitor won't be confused because of it. + +### Async works on inline scripts + +Async attribute ` +``` + +### External scripts + +There are two notable differences of external module scripts: + +1. External scripts with same `src` run only once: + ```html + + + + ``` + +2. External scripts that are fetched from another domain require [CORS](mdn:Web/HTTP/CORS) headers. In other words, if a module script is fetched from another domain, the remote server must supply a header `Access-Control-Allow-Origin: *` (may use fetching domain instead of `*`) to indicate that the fetch is allowed. + ```html + + + + ``` + + That ensures better security by default. + +### No bare modules allowed + +In the browser, in scripts (not in HTML), `import` must get either a relative or absolute URL. So-called "bare" modules, without a path, are not allowed. + +For instance, this `import` is invalid: +```js +import {sayHi} from 'sayHi'; // Error, "bare" module +// must be './sayHi.js' or wherever the module is +``` + +Certain environments, like Node.js or bundle tools allow bare modules, as they have own ways for finding modules and hooks to fine-tune them. But browsers do not support bare modules yet. + +### Compatibility, "nomodule" + +Old browsers do not understand `type="module"`. Scripts of the unknown type are just ignored. For them, it's possible to provide a fallback using `nomodule` attribute: + +```html run + + + +``` + +If we use bundle tools, then as modules are bundled together, their `import/export` statements are replaced by special bundler calls, so the resulting build does not require `type="module"`, and we can put it into a regular script: + +```html + + +``` + +## Build tools + +In real-life, browser modules are rarely used in their "raw" form. Usually, we bundle them together with a special tool such as [Webpack](https://webpack.js.org/) and deploy to the production server. + +One of the benefits of using bundlers -- they give more control over how modules are resolved, allowing bare modules and much more, like CSS/HTML modules. + +Build tools do the following: + +1. Take a "main" module, the one intended to be put in ` diff --git a/1-js/12-modules/01-modules-intro/scopes-working.view/user.js b/1-js/12-modules/01-modules-intro/scopes-working.view/user.js new file mode 100644 index 00000000..d289329c --- /dev/null +++ b/1-js/12-modules/01-modules-intro/scopes-working.view/user.js @@ -0,0 +1 @@ +export let user = "John"; diff --git a/1-js/12-modules/01-modules-intro/scopes.view/hello.js b/1-js/12-modules/01-modules-intro/scopes.view/hello.js new file mode 100644 index 00000000..714aafa1 --- /dev/null +++ b/1-js/12-modules/01-modules-intro/scopes.view/hello.js @@ -0,0 +1 @@ +alert(user); // no such variable (each module has independent variables) diff --git a/1-js/12-modules/01-modules-intro/scopes.view/index.html b/1-js/12-modules/01-modules-intro/scopes.view/index.html new file mode 100644 index 00000000..a87e96fd --- /dev/null +++ b/1-js/12-modules/01-modules-intro/scopes.view/index.html @@ -0,0 +1,3 @@ + + + diff --git a/1-js/12-modules/01-modules-intro/scopes.view/user.js b/1-js/12-modules/01-modules-intro/scopes.view/user.js new file mode 100644 index 00000000..12ec850d --- /dev/null +++ b/1-js/12-modules/01-modules-intro/scopes.view/user.js @@ -0,0 +1 @@ +let user = "John"; diff --git a/1-js/10-modules/02-import-export/article.md b/1-js/12-modules/02-import-export/article.md similarity index 88% rename from 1-js/10-modules/02-import-export/article.md rename to 1-js/12-modules/02-import-export/article.md index d8ee7ea9..dbeb1540 100644 --- a/1-js/10-modules/02-import-export/article.md +++ b/1-js/12-modules/02-import-export/article.md @@ -31,7 +31,7 @@ Please note that `export` before a class or a function does not make it a [funct Most Javascript style guides recommend semicolons after statements, but not after function and class declarations. -That's why there should bes no semicolons at the end of `export class` and `export function`. +That's why there should be no semicolons at the end of `export class` and `export function`. ```js export function sayHi(user) { @@ -41,7 +41,7 @@ export function sayHi(user) { ```` -## Export after declarations +## Export apart from declarations Also, we can put `export` separately. @@ -228,15 +228,6 @@ export class { // Error! (non-default export needs a name) } ``` -When importing, to keep the code consistent, teams usually employ the rule that local variables should be named after files, e.g: - -```js -import User from './user.js'; -import LoginForm from './loginForm.js'; -import func from '/path/to/func.js'; -... -``` - ### "Default" alias The "default" word is a kind of "alias" for the default export, for scenarios when we need to reference it somehow. @@ -286,6 +277,38 @@ new User('John'); ``` +### Should I use default exports? + +One should be careful about using default exports, because they are somewhat more different to maintain. + +Named exports are explicit. They exactly name what they import, so we have that information from them, that's a good thing. + +Also, named exports enforce us to use exactly the right name to import: + +```js +import {User} from './user.js'; +``` + +For default exports, we need to create a name on our own: + +```js +import MyUser from './user.js'; // could be import Anything..., and it'll work +``` + +So, there's a little bit more freedom that can be abused, so that team members may use different names for the same thing. + +Usually, to avoid that and keep the code consistent, there's a rule that imported variables should correspond to file names, e.g: + +```js +import User from './user.js'; +import LoginForm from './loginForm.js'; +import func from '/path/to/func.js'; +... +``` + +Another solution would be to use named exports everywhere. Even if only a single thing is exported, it's still exported under a name, without `default`. + +That also makes re-export (see below) a little bit easier. ## Re-export @@ -401,13 +424,15 @@ import {sayHi} from './say.js'; // import at the end of the file In practice imports are usually at the start of the file, but that's only for better convenience. -**Please note that all import/export statements must be at the top level of the script.** +**Please note that import/export statements don't work if inside `{...}`.** -This won't work: +A conditional import, like this, won't work: ```js if (something) { import {sayHi} from "./say.js"; // Error: import must be at top level } ``` -...But what if we really need to import something dynamically? Like, load a module upon request, when it's really needed? We'll see how to do it in the next chapter. +...But what if we really need to import something conditionally? Or at the right time? Like, load a module upon request, when it's really needed? + +We'll see dynamic imports in the next chapter. diff --git a/1-js/12-modules/03-modules-dynamic-imports/article.md b/1-js/12-modules/03-modules-dynamic-imports/article.md new file mode 100644 index 00000000..513cdf89 --- /dev/null +++ b/1-js/12-modules/03-modules-dynamic-imports/article.md @@ -0,0 +1,54 @@ + +# Dynamic imports + +Export and import statements that we covered in previous chaters are called "static". + +What's because they are indeed static. The syntax is very strict. + +First, we can't dynamicaly generate any parameters of `import`. + +The module path must be a primitive string, can't be a function call. This won't work: + +```js +import ... from *!*getModuleName()*/!*; // Error, only from "string" is allowed +``` + +Second, we can't import conditionally or at run-time: + +```js +if(...) { + import ...; // Error, not allowed! +} + +{ + import ...; // Error, we can't put import in any block +} +``` + +That's because, import/export aim to provide a backbone for the code structure. That's a good thing, as code structure can be analyzed, modules can be gathered and bundled together, unused exports can be removed (tree-shaken). That's possible only because everything is fixed. + +But how do we import a module dynamically, on-demand? + +## The import() function + +The `import(module)` function can be called from anywhere. It returns a promise that resolves into a module object. + +The usage pattern looks like this: + +```js run +let modulePath = prompt("Module path?"); + +import(modulePath) + .then(obj => ) + .catch(err => ) +``` + +Or, we could use `let module = await import(modulePath)` if inside an async function. + +Like this: + +[codetabs src="say" current="index.html"] + +So, dynamic imports are very simple to use. + +Also, dynamic imports work in regular scripts, they don't require `script type="module"`. diff --git a/1-js/10-modules/03-modules-dynamic-imports/say.view/index.html b/1-js/12-modules/03-modules-dynamic-imports/say.view/index.html similarity index 100% rename from 1-js/10-modules/03-modules-dynamic-imports/say.view/index.html rename to 1-js/12-modules/03-modules-dynamic-imports/say.view/index.html index 6cd9734f..80909cf9 100644 --- a/1-js/10-modules/03-modules-dynamic-imports/say.view/index.html +++ b/1-js/12-modules/03-modules-dynamic-imports/say.view/index.html @@ -1,5 +1,4 @@ - + diff --git a/1-js/10-modules/03-modules-dynamic-imports/say.view/say.js b/1-js/12-modules/03-modules-dynamic-imports/say.view/say.js similarity index 100% rename from 1-js/10-modules/03-modules-dynamic-imports/say.view/say.js rename to 1-js/12-modules/03-modules-dynamic-imports/say.view/say.js diff --git a/1-js/10-modules/index.md b/1-js/12-modules/index.md similarity index 100% rename from 1-js/10-modules/index.md rename to 1-js/12-modules/index.md diff --git a/figures.sketch b/figures.sketch index 05b1da88..c6416d60 100644 Binary files a/figures.sketch and b/figures.sketch differ