-
-Debounced function debounce(handler, 1000) is called on this input:
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/solution.md b/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/solution.md
index 83e75f31..4f5867de 100644
--- a/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/solution.md
+++ b/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/solution.md
@@ -1,13 +1,28 @@
```js demo
-function debounce(func, ms) {
- let timeout;
- return function() {
- clearTimeout(timeout);
- timeout = setTimeout(() => func.apply(this, arguments), ms);
- };
-}
+function debounce(f, ms) {
+ let isCooldown = false;
+
+ return function() {
+ if (isCooldown) return;
+
+ f.apply(this, arguments);
+
+ isCooldown = true;
+
+ setTimeout(() => isCooldown = false, ms);
+ };
+
+}
```
-A call to `debounce` returns a wrapper. When called, it schedules the original function call after given `ms` and cancels the previous such timeout.
+A call to `debounce` returns a wrapper. There may be two states:
+- `isCooldown = false` -- ready to run.
+- `isCooldown = true` -- waiting for the timeout.
+
+In the first call `isCooldown` is falsy, so the call proceeds, and the state changes to `true`.
+
+While `isCooldown` is true, all other calls are ignored.
+
+Then `setTimeout` reverts it to `false` after the given delay.
diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/task.md b/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/task.md
index 5b0fcc5f..2620f1c7 100644
--- a/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/task.md
+++ b/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/task.md
@@ -4,48 +4,21 @@ importance: 5
# Debounce decorator
-The result of `debounce(f, ms)` decorator is a wrapper that suspends calls to `f` until there's `ms` milliseconds of inactivity (no calls, "cooldown period"), then invokes `f` once with the latest arguments.
+The result of `debounce(f, ms)` decorator should be a wrapper that passes the call to `f` at maximum once per `ms` milliseconds.
-In other words, `debounce` is like a secretary that accepts "phone calls", and waits until there's `ms` milliseconds of being quiet. And only then it transfers the latest call information to "the boss" (calls the actual `f`).
+In other words, when we call a "debounced" function, it guarantees that all future calls to the function made less than `ms` milliseconds after the previous call will be ignored.
-For instance, we had a function `f` and replaced it with `f = debounce(f, 1000)`.
+For instance:
-Then if the wrapped function is called at 0ms, 200ms and 500ms, and then there are no calls, then the actual `f` will be only called once, at 1500ms. That is: after the cooldown period of 1000ms from the last call.
+```js no-beautify
+let f = debounce(alert, 1000);
-
+f(1); // runs immediately
+f(2); // ignored
-...And it will get the arguments of the very last call, other calls are ignored.
-
-Here's the code for it (uses the debounce decorator from the [Lodash library](https://lodash.com/docs/4.17.15#debounce)):
-
-```js
-let f = _.debounce(alert, 1000);
-
-f("a");
-setTimeout( () => f("b"), 200);
-setTimeout( () => f("c"), 500);
-// debounced function waits 1000ms after the last call and then runs: alert("c")
+setTimeout( () => f(3), 100); // ignored ( only 100 ms passed )
+setTimeout( () => f(4), 1100); // runs
+setTimeout( () => f(5), 1500); // ignored (less than 1000 ms from the last run)
```
-Now a practical example. Let's say, the user types something, and we'd like to send a request to the server when the input is finished.
-
-There's no point in sending the request for every character typed. Instead we'd like to wait, and then process the whole result.
-
-In a web-browser, we can setup an event handler -- a function that's called on every change of an input field. Normally, an event handler is called very often, for every typed key. But if we `debounce` it by 1000ms, then it will be only called once, after 1000ms after the last input.
-
-```online
-
-In this live example, the handler puts the result into a box below, try it:
-
-[iframe border=1 src="debounce" height=200]
-
-See? The second input calls the debounced function, so its content is processed after 1000ms from the last input.
-```
-
-So, `debounce` is a great way to process a sequence of events: be it a sequence of key presses, mouse movements or something else.
-
-It waits the given time after the last call, and then runs its function, that can process the result.
-
-The task is to implement `debounce` decorator.
-
-Hint: that's just a few lines if you think about it :)
+In practice `debounce` is useful for functions that retrieve/update something when we know that nothing new can be done in such a short period of time, so it's better not to waste resources.
diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/_js.view/test.js b/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/_js.view/test.js
index e671438f..5339c8d1 100644
--- a/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/_js.view/test.js
+++ b/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/_js.view/test.js
@@ -7,8 +7,8 @@ describe("throttle(f, 1000)", function() {
}
before(function() {
- this.clock = sinon.useFakeTimers();
f1000 = throttle(f, 1000);
+ this.clock = sinon.useFakeTimers();
});
it("the first call runs now", function() {
@@ -44,20 +44,4 @@ describe("throttle(f, 1000)", function() {
this.clock.restore();
});
-});
-
-describe('throttle', () => {
-
- it('runs a forwarded call once', done => {
- let log = '';
- const f = str => log += str;
- const f10 = throttle(f, 10);
- f10('once');
-
- setTimeout(() => {
- assert.equal(log, 'once');
- done();
- }, 20);
- });
-
-});
+});
\ No newline at end of file
diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/solution.md b/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/solution.md
index 6950664b..c844016d 100644
--- a/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/solution.md
+++ b/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/solution.md
@@ -12,10 +12,11 @@ function throttle(func, ms) {
savedThis = this;
return;
}
- isThrottled = true;
func.apply(this, arguments); // (1)
+ isThrottled = true;
+
setTimeout(function() {
isThrottled = false; // (3)
if (savedArgs) {
@@ -32,7 +33,7 @@ function throttle(func, ms) {
A call to `throttle(func, ms)` returns `wrapper`.
1. During the first call, the `wrapper` just runs `func` and sets the cooldown state (`isThrottled = true`).
-2. In this state all calls are memorized in `savedArgs/savedThis`. Please note that both the context and the arguments are equally important and should be memorized. We need them simultaneously to reproduce the call.
-3. After `ms` milliseconds pass, `setTimeout` triggers. The cooldown state is removed (`isThrottled = false`) and, if we had ignored calls, `wrapper` is executed with the last memorized arguments and context.
+2. In this state all calls memorized in `savedArgs/savedThis`. Please note that both the context and the arguments are equally important and should be memorized. We need them simultaneously to reproduce the call.
+3. ...Then after `ms` milliseconds pass, `setTimeout` triggers. The cooldown state is removed (`isThrottled = false`). And if we had ignored calls, then `wrapper` is executed with last memorized arguments and context.
The 3rd step runs not `func`, but `wrapper`, because we not only need to execute `func`, but once again enter the cooldown state and setup the timeout to reset it.
diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/task.md b/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/task.md
index cbd47319..567c9ce7 100644
--- a/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/task.md
+++ b/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/task.md
@@ -4,21 +4,16 @@ importance: 5
# Throttle decorator
-Create a "throttling" decorator `throttle(f, ms)` -- that returns a wrapper.
+Create a "throttling" decorator `throttle(f, ms)` -- that returns a wrapper, passing the call to `f` at maximum once per `ms` milliseconds. Those calls that fall into the "cooldown" period, are ignored.
-When it's called multiple times, it passes the call to `f` at maximum once per `ms` milliseconds.
-
-Compared to the debounce decorator, the behavior is completely different:
-- `debounce` runs the function once after the "cooldown" period. Good for processing the final result.
-- `throttle` runs it not more often than given `ms` time. Good for regular updates that shouldn't be very often.
-
-In other words, `throttle` is like a secretary that accepts phone calls, but bothers the boss (calls the actual `f`) not more often than once per `ms` milliseconds.
+**The difference with `debounce` -- if an ignored call is the last during the cooldown, then it executes at the end of the delay.**
Let's check the real-life application to better understand that requirement and to see where it comes from.
**For instance, we want to track mouse movements.**
-In a browser we can setup a function to run at every mouse movement and get the pointer location as it moves. During an active mouse usage, this function usually runs very frequently, can be something like 100 times per second (every 10 ms).
+In browser we can setup a function to run at every mouse movement and get the pointer location as it moves. During an active mouse usage, this function usually runs very frequently, can be something like 100 times per second (every 10 ms).
+
**We'd like to update some information on the web-page when the pointer moves.**
...But updating function `update()` is too heavy to do it on every micro-movement. There is also no sense in updating more often than once per 100ms.
@@ -36,8 +31,8 @@ A code example:
```js
function f(a) {
- console.log(a);
-}
+ console.log(a)
+};
// f1000 passes calls to f at maximum once per 1000 ms
let f1000 = throttle(f, 1000);
diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/article.md b/1-js/06-advanced-functions/09-call-apply-decorators/article.md
index c5d78549..8536cf31 100644
--- a/1-js/06-advanced-functions/09-call-apply-decorators/article.md
+++ b/1-js/06-advanced-functions/09-call-apply-decorators/article.md
@@ -36,11 +36,11 @@ function cachingDecorator(func) {
slow = cachingDecorator(slow);
-alert( slow(1) ); // slow(1) is cached and the result returned
-alert( "Again: " + slow(1) ); // slow(1) result returned from cache
+alert( slow(1) ); // slow(1) is cached
+alert( "Again: " + slow(1) ); // the same
-alert( slow(2) ); // slow(2) is cached and the result returned
-alert( "Again: " + slow(2) ); // slow(2) result returned from cache
+alert( slow(2) ); // slow(2) is cached
+alert( "Again: " + slow(2) ); // the same as the previous line
```
In the code above `cachingDecorator` is a *decorator*: a special function that takes another function and alters its behavior.
@@ -75,7 +75,7 @@ let worker = {
},
slow(x) {
- // scary CPU-heavy task here
+ // actually, there can be a scary CPU-heavy task here
alert("Called with " + x);
return x * this.someMethod(); // (*)
}
@@ -149,8 +149,8 @@ let user = { name: "John" };
let admin = { name: "Admin" };
// use call to pass different objects as "this"
-sayHi.call( user ); // John
-sayHi.call( admin ); // Admin
+sayHi.call( user ); // this = John
+sayHi.call( admin ); // this = Admin
```
And here we use `call` to call `say` with the given context and phrase:
@@ -209,7 +209,7 @@ To make it all clear, let's see more deeply how `this` is passed along:
2. So when `worker.slow(2)` is executed, the wrapper gets `2` as an argument and `this=worker` (it's the object before dot).
3. Inside the wrapper, assuming the result is not yet cached, `func.call(this, x)` passes the current `this` (`=worker`) and the current argument (`=2`) to the original method.
-## Going multi-argument
+## Going multi-argument with "func.apply"
Now let's make `cachingDecorator` even more universal. Till now it was working only with single-argument functions.
@@ -236,7 +236,7 @@ There are many solutions possible:
For many practical applications, the 3rd variant is good enough, so we'll stick to it.
-Also we need to pass not just `x`, but all arguments in `func.call`. Let's recall that in a `function()` we can get a pseudo-array of its arguments as `arguments`, so `func.call(this, x)` should be replaced with `func.call(this, ...arguments)`.
+Also we need to replace `func.call(this, x)` with `func.call(this, ...arguments)`, to pass all arguments to the wrapped function call, not just the first one.
Here's a more powerful `cachingDecorator`:
@@ -284,8 +284,6 @@ There are two changes:
- In the line `(*)` it calls `hash` to create a single key from `arguments`. Here we use a simple "joining" function that turns arguments `(3, 5)` into the key `"3,5"`. More complex cases may require other hashing functions.
- Then `(**)` uses `func.call(this, ...arguments)` to pass both the context and all arguments the wrapper got (not just the first one) to the original function.
-## func.apply
-
Instead of `func.call(this, ...arguments)` we could use `func.apply(this, arguments)`.
The syntax of built-in method [func.apply](mdn:js/Function/apply) is:
@@ -301,18 +299,18 @@ The only syntax difference between `call` and `apply` is that `call` expects a l
So these two calls are almost equivalent:
```js
-func.call(context, ...args);
-func.apply(context, args);
+func.call(context, ...args); // pass an array as list with spread operator
+func.apply(context, args); // is same as using apply
```
-They perform the same call of `func` with given context and arguments.
+There's only a minor difference:
-There's only a subtle difference regarding `args`:
-
-- The spread syntax `...` allows to pass *iterable* `args` as the list to `call`.
+- The spread operator `...` allows to pass *iterable* `args` as the list to `call`.
- The `apply` accepts only *array-like* `args`.
-...And for objects that are both iterable and array-like, such as a real array, we can use any of them, but `apply` will probably be faster, because most JavaScript engines internally optimize it better.
+So, these calls complement each other. Where we expect an iterable, `call` works, where we expect an array-like, `apply` works.
+
+And for objects that are both iterable and array-like, like a real array, we technically could use any of them, but `apply` will probably be faster, because most JavaScript engines internally optimize it better.
Passing all arguments along with the context to another function is called *call forwarding*.
@@ -346,7 +344,7 @@ function hash(args) {
}
```
-...Unfortunately, that won't work. Because we are calling `hash(arguments)`, and `arguments` object is both iterable and array-like, but not a real array.
+...Unfortunately, that won't work. Because we are calling `hash(arguments)` and `arguments` object is both iterable and array-like, but not a real array.
So calling `join` on it would fail, as we can see below:
@@ -374,7 +372,7 @@ hash(1, 2);
The trick is called *method borrowing*.
-We take (borrow) a join method from a regular array (`[].join`) and use `[].join.call` to run it in the context of `arguments`.
+We take (borrow) a join method from a regular array `[].join`. And use `[].join.call` to run it in the context of `arguments`.
Why does it work?
diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/decorator-makecaching-wrapper.svg b/1-js/06-advanced-functions/09-call-apply-decorators/decorator-makecaching-wrapper.svg
index 9b63cb98..258fcfdf 100644
--- a/1-js/06-advanced-functions/09-call-apply-decorators/decorator-makecaching-wrapper.svg
+++ b/1-js/06-advanced-functions/09-call-apply-decorators/decorator-makecaching-wrapper.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/06-advanced-functions/10-bind/6-ask-partial/task.md b/1-js/06-advanced-functions/10-bind/6-ask-partial/task.md
index c90851c2..f8b83d7a 100644
--- a/1-js/06-advanced-functions/10-bind/6-ask-partial/task.md
+++ b/1-js/06-advanced-functions/10-bind/6-ask-partial/task.md
@@ -8,7 +8,7 @@ The task is a little more complex variant of .
The `user` object was modified. Now instead of two functions `loginOk/loginFail`, it has a single function `user.login(true/false)`.
-What should we pass `askPassword` in the code below, so that it calls `user.login(true)` as `ok` and `user.login(false)` as `fail`?
+What to pass `askPassword` in the code below, so that it calls `user.login(true)` as `ok` and `user.login(false)` as `fail`?
```js
function askPassword(ok, fail) {
diff --git a/1-js/06-advanced-functions/10-bind/article.md b/1-js/06-advanced-functions/10-bind/article.md
index 9d705cdc..b8c545b1 100644
--- a/1-js/06-advanced-functions/10-bind/article.md
+++ b/1-js/06-advanced-functions/10-bind/article.md
@@ -83,12 +83,10 @@ let user = {
setTimeout(() => user.sayHi(), 1000);
-// ...the value of user changes within 1 second
-user = {
- sayHi() { alert("Another user in setTimeout!"); }
-};
+// ...within 1 second
+user = { sayHi() { alert("Another user in setTimeout!"); } };
-// Another user in setTimeout!
+// Another user in setTimeout?!?
```
The next solution guarantees that such thing won't happen.
@@ -100,7 +98,7 @@ Functions provide a built-in method [bind](mdn:js/Function/bind) that allows to
The basic syntax is:
```js
-// more complex syntax will come a little later
+// more complex syntax will be little later
let boundFunc = func.bind(context);
```
@@ -161,16 +159,9 @@ let user = {
let sayHi = user.sayHi.bind(user); // (*)
*/!*
-// can run it without an object
sayHi(); // Hello, John!
setTimeout(sayHi, 1000); // Hello, John!
-
-// even if the value of user changes within 1 second
-// sayHi uses the pre-bound value which is reference to the old user object
-user = {
- sayHi() { alert("Another user in setTimeout!"); }
-};
```
In the line `(*)` we take the method `user.sayHi` and bind it to `user`. The `sayHi` is a "bound" function, that can be called alone or passed to `setTimeout` -- doesn't matter, the context will be right.
@@ -187,8 +178,8 @@ let user = {
let say = user.say.bind(user);
-say("Hello"); // Hello, John! ("Hello" argument is passed to say)
-say("Bye"); // Bye, John! ("Bye" is passed to say)
+say("Hello"); // Hello, John ("Hello" argument is passed to say)
+say("Bye"); // Bye, John ("Bye" is passed to say)
```
````smart header="Convenience method: `bindAll`"
@@ -202,7 +193,7 @@ for (let key in user) {
}
```
-JavaScript libraries also provide functions for convenient mass binding , e.g. [_.bindAll(object, methodNames)](http://lodash.com/docs#bindAll) in lodash.
+JavaScript libraries also provide functions for convenient mass binding , e.g. [_.bindAll(obj)](http://lodash.com/docs#bindAll) in lodash.
````
## Partial functions
@@ -247,7 +238,7 @@ The call to `mul.bind(null, 2)` creates a new function `double` that passes call
That's called [partial function application](https://en.wikipedia.org/wiki/Partial_application) -- we create a new function by fixing some parameters of the existing one.
-Please note that we actually don't use `this` here. But `bind` requires it, so we must put in something like `null`.
+Please note that here we actually don't use `this` here. But `bind` requires it, so we must put in something like `null`.
The function `triple` in the code below triples the value:
@@ -279,7 +270,7 @@ What if we'd like to fix some arguments, but not the context `this`? For example
The native `bind` does not allow that. We can't just omit the context and jump to arguments.
-Fortunately, a function `partial` for binding only arguments can be easily implemented.
+Fortunately, a helper function `partial` for binding only arguments can be easily implemented.
Like this:
@@ -313,7 +304,7 @@ The result of `partial(func[, arg1, arg2...])` call is a wrapper `(*)` that call
- Then gives it `...argsBound` -- arguments from the `partial` call (`"10:00"`)
- Then gives it `...args` -- arguments given to the wrapper (`"Hello"`)
-So easy to do it with the spread syntax, right?
+So easy to do it with the spread operator, right?
Also there's a ready [_.partial](https://lodash.com/docs#partial) implementation from lodash library.
diff --git a/1-js/06-advanced-functions/12-arrow-functions/article.md b/1-js/06-advanced-functions/12-arrow-functions/article.md
index 8730277a..1d6b0439 100644
--- a/1-js/06-advanced-functions/12-arrow-functions/article.md
+++ b/1-js/06-advanced-functions/12-arrow-functions/article.md
@@ -52,7 +52,7 @@ let group = {
*!*
this.students.forEach(function(student) {
// Error: Cannot read property 'title' of undefined
- alert(this.title + ': ' + student);
+ alert(this.title + ': ' + student)
});
*/!*
}
@@ -87,7 +87,7 @@ For instance, `defer(f, ms)` gets a function and returns a wrapper around it tha
```js run
function defer(f, ms) {
return function() {
- setTimeout(() => f.apply(this, arguments), ms);
+ setTimeout(() => f.apply(this, arguments), ms)
};
}
@@ -118,9 +118,9 @@ Here we had to create additional variables `args` and `ctx` so that the function
Arrow functions:
-- Do not have `this`
-- Do not have `arguments`
-- Can't be called with `new`
-- They also don't have `super`, but we didn't study it yet. We will on the chapter
+- Do not have `this`.
+- Do not have `arguments`.
+- Can't be called with `new`.
+- (They also don't have `super`, but we didn't study it. Will be in the chapter ).
-That's because they are meant for short pieces of code that do not have their own "context", but rather work in the current one. And they really shine in that use case.
+That's because they are meant for short pieces of code that do not have their own "context", but rather works in the current one. And they really shine in that use case.
diff --git a/1-js/07-object-properties/01-property-descriptors/article.md b/1-js/07-object-properties/01-property-descriptors/article.md
index bdc69341..9f8f85d9 100644
--- a/1-js/07-object-properties/01-property-descriptors/article.md
+++ b/1-js/07-object-properties/01-property-descriptors/article.md
@@ -3,7 +3,7 @@
As we know, objects can store properties.
-Until now, a property was a simple "key-value" pair to us. But an object property is actually a more flexible and powerful 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.
@@ -19,7 +19,7 @@ We didn't see them yet, because generally they do not show up. When we create a
First, let's see how to get those flags.
-The method [Object.getOwnPropertyDescriptor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor) allows to query the *full* information about a property.
+The method [Object.getOwnPropertyDescriptor](mdn:js/Object/getOwnPropertyDescriptor) allows to query the *full* information about a property.
The syntax is:
```js
@@ -54,7 +54,7 @@ alert( JSON.stringify(descriptor, null, 2 ) );
*/
```
-To change the flags, we can use [Object.defineProperty](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty).
+To change the flags, we can use [Object.defineProperty](mdn:js/Object/defineProperty).
The syntax is:
@@ -66,7 +66,7 @@ Object.defineProperty(obj, propertyName, descriptor)
: The object and its property to apply the descriptor.
`descriptor`
-: Property descriptor object to apply.
+: Property descriptor to apply.
If the property exists, `defineProperty` updates its flags. Otherwise, it creates the property with the given value and flags; in that case, if a flag is not supplied, it is assumed `false`.
@@ -134,7 +134,7 @@ let user = { };
Object.defineProperty(user, "name", {
*!*
value: "John",
- // for new properties we need to explicitly list what's true
+ // for new properties need to explicitly list what's true
enumerable: true,
configurable: true
*/!*
@@ -148,7 +148,7 @@ user.name = "Pete"; // Error
Now let's add a custom `toString` to `user`.
-Normally, a built-in `toString` for objects is non-enumerable, it does not show up in `for..in`. But if we add a `toString` of our own, then by default it shows up in `for..in`, like this:
+Normally, a built-in `toString` for objects is non-enumerable, it does not show up in `for..in`. But if we add `toString` of our own, then by default it shows up in `for..in`, like this:
```js run
let user = {
@@ -162,7 +162,7 @@ let user = {
for (let key in user) alert(key); // name, toString
```
-If we don't like it, then we can set `enumerable:false`. Then it won't appear in a `for..in` loop, just like the built-in one:
+If we don't like it, then we can set `enumerable:false`. Then it won't appear in `for..in` loop, just like the built-in one:
```js run
let user = {
@@ -194,7 +194,7 @@ alert(Object.keys(user)); // name
The non-configurable flag (`configurable:false`) is sometimes preset for built-in objects and properties.
-A non-configurable property can't be deleted, its attributes can't be modified.
+A non-configurable property can not be deleted.
For instance, `Math.PI` is non-writable, non-enumerable and non-configurable:
@@ -214,67 +214,49 @@ alert( JSON.stringify(descriptor, null, 2 ) );
So, a programmer is unable to change the value of `Math.PI` or overwrite it.
```js run
-Math.PI = 3; // Error, because it has writable: false
+Math.PI = 3; // Error
// delete Math.PI won't work either
```
-We also can't change `Math.PI` to be `writable` again:
-
-```js run
-// Error, because of configurable: false
-Object.defineProperty(Math, "PI", { writable: true });
-```
-
-There's absolutely nothing we can do with `Math.PI`.
-
Making a property non-configurable is a one-way road. We cannot change it back with `defineProperty`.
-**Please note: `configurable: false` prevents changes of property flags and its deletion, while allowing to change its value.**
+To be precise, non-configurability imposes several restrictions on `defineProperty`:
+1. Can't change `configurable` flag.
+2. Can't change `enumerable` flag.
+3. Can't change `writable: false` to `true` (the other way round works).
+4. Can't change `get/set` for an accessor property (but can assign them if absent).
-Here `user.name` is non-configurable, but we can still change it (as it's writable):
+Here we are making `user.name` a "forever sealed" constant:
```js run
-let user = {
- name: "John"
-};
-
-Object.defineProperty(user, "name", {
- configurable: false
-});
-
-user.name = "Pete"; // works fine
-delete user.name; // Error
-```
-
-And here we make `user.name` a "forever sealed" constant, just like the built-in `Math.PI`:
-
-```js run
-let user = {
- name: "John"
-};
+let user = { };
Object.defineProperty(user, "name", {
+ value: "John",
writable: false,
configurable: false
});
+*!*
// won't be able to change user.name or its flags
// all this won't work:
-user.name = "Pete";
-delete user.name;
-Object.defineProperty(user, "name", { value: "Pete" });
+// user.name = "Pete"
+// delete user.name
+// defineProperty(user, "name", { value: "Pete" })
+Object.defineProperty(user, "name", {writable: true}); // Error
+*/!*
```
-```smart header="The only attribute change possible: writable true -> false"
-There's a minor exception about changing flags.
+```smart header="\"Non-configurable\" doesn't mean \"non-writable\""
+Notable exception: a value of non-configurable, but writable property can be changed.
-We can change `writable: true` to `false` for a non-configurable property, thus preventing its value modification (to add another layer of protection). Not the other way around though.
+The idea of `configurable: false` is to prevent changes to property flags and its deletion, not changes to its value.
```
## Object.defineProperties
-There's a method [Object.defineProperties(obj, descriptors)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties) that allows to define many properties at once.
+There's a method [Object.defineProperties(obj, descriptors)](mdn:js/Object/defineProperties) that allows to define many properties at once.
The syntax is:
@@ -300,7 +282,7 @@ So, we can set many properties at once.
## Object.getOwnPropertyDescriptors
-To get all property descriptors at once, we can use the method [Object.getOwnPropertyDescriptors(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptors).
+To get all property descriptors at once, we can use the method [Object.getOwnPropertyDescriptors(obj)](mdn:js/Object/getOwnPropertyDescriptors).
Together with `Object.defineProperties` it can be used as a "flags-aware" way of cloning an object:
@@ -318,7 +300,7 @@ for (let key in user) {
...But that does not copy flags. So if we want a "better" clone then `Object.defineProperties` is preferred.
-Another difference is that `for..in` ignores symbolic and non-enumerable properties, but `Object.getOwnPropertyDescriptors` returns *all* property descriptors including symbolic and non-enumerable ones.
+Another difference is that `for..in` ignores symbolic properties, but `Object.getOwnPropertyDescriptors` returns *all* property descriptors including symbolic ones.
## Sealing an object globally
@@ -326,24 +308,24 @@ Property descriptors work at the level of individual properties.
There are also methods that limit access to the *whole* object:
-[Object.preventExtensions(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/preventExtensions)
+[Object.preventExtensions(obj)](mdn:js/Object/preventExtensions)
: Forbids the addition of new properties to the object.
-[Object.seal(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal)
+[Object.seal(obj)](mdn:js/Object/seal)
: Forbids adding/removing of properties. Sets `configurable: false` for all existing properties.
-[Object.freeze(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze)
+[Object.freeze(obj)](mdn:js/Object/freeze)
: Forbids adding/removing/changing of properties. Sets `configurable: false, writable: false` for all existing properties.
And also there are tests for them:
-[Object.isExtensible(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible)
+[Object.isExtensible(obj)](mdn:js/Object/isExtensible)
: Returns `false` if adding properties is forbidden, otherwise `true`.
-[Object.isSealed(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed)
+[Object.isSealed(obj)](mdn:js/Object/isSealed)
: Returns `true` if adding/removing properties is forbidden, and all existing properties have `configurable: false`.
-[Object.isFrozen(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen)
+[Object.isFrozen(obj)](mdn:js/Object/isFrozen)
: Returns `true` if adding/removing/changing properties is forbidden, and all current properties are `configurable: false, writable: false`.
These methods are rarely used in practice.
diff --git a/1-js/07-object-properties/02-property-accessors/article.md b/1-js/07-object-properties/02-property-accessors/article.md
index c2aa35d5..dc541b6d 100644
--- a/1-js/07-object-properties/02-property-accessors/article.md
+++ b/1-js/07-object-properties/02-property-accessors/article.md
@@ -1,11 +1,11 @@
# Property getters and setters
-There are two kinds of object properties.
+There are two kinds of properties.
-The first kind is *data properties*. We already know how to work with them. All properties that we've been using until now were data properties.
+The first kind is *data properties*. We already know how to work with them. All properties that we've been using till now were data properties.
-The second type of property is something new. It's an *accessor property*. They are essentially functions that execute on getting and setting a value, but look like regular properties to an external code.
+The second type of properties is something new. It's *accessor properties*. They are essentially functions that work on getting and setting a value, but look like regular properties to an external code.
## Getters and setters
@@ -53,7 +53,7 @@ alert(user.fullName); // John Smith
*/!*
```
-From the outside, an accessor property looks like a regular one. That's the idea of accessor properties. We don't *call* `user.fullName` as a function, we *read* it normally: the getter runs behind the scenes.
+From outside, an accessor property looks like a regular one. That's the idea of accessor properties. We don't *call* `user.fullName` as a function, we *read* it normally: the getter runs behind the scenes.
As of now, `fullName` has only a getter. If we attempt to assign `user.fullName=`, there will be an error:
@@ -94,7 +94,11 @@ alert(user.name); // Alice
alert(user.surname); // Cooper
```
-As the result, we have a "virtual" property `fullName`. It is readable and writable.
+As the result, we have a "virtual" property `fullName`. It is readable and writable, but in fact does not exist.
+
+```smart header="No way to handle `delete`"
+There's no similar method to handle deletion of an accessor property. Only getter/setter methods may exist.
+```
## Accessor descriptors
@@ -134,7 +138,7 @@ alert(user.fullName); // John Smith
for(let key in user) alert(key); // name, surname
```
-Please note that a property can be either an accessor (has `get/set` methods) or a data property (has a `value`), not both.
+Please note once again that a property can be either an accessor (has `get/set` methods) or a data property (has a `value`), not both.
If we try to supply both `get` and `value` in the same descriptor, there will be an error:
@@ -185,9 +189,9 @@ Technically, external code is able to access the name directly by using `user._n
## Using for compatibility
-One of the great uses of accessors is that they allow to take control over a "regular" data property at any moment by replacing it with a getter and a setter and tweak its behavior.
+One of the great uses of accessors -- they allow to take control over a "regular" data property at any moment by replacing it with getter and setter and tweak its behavior.
-Imagine we started implementing user objects using data properties `name` and `age`:
+Imagine, we started implementing user objects using data properties `name` and `age`:
```js
function User(name, age) {
diff --git a/1-js/08-prototypes/01-prototype-inheritance/2-search-algorithm/task.md b/1-js/08-prototypes/01-prototype-inheritance/2-search-algorithm/task.md
index bc2db47f..421b57e0 100644
--- a/1-js/08-prototypes/01-prototype-inheritance/2-search-algorithm/task.md
+++ b/1-js/08-prototypes/01-prototype-inheritance/2-search-algorithm/task.md
@@ -6,7 +6,7 @@ importance: 5
The task has two parts.
-Given the following objects:
+We have objects:
```js
let head = {
diff --git a/1-js/08-prototypes/01-prototype-inheritance/3-proto-and-this/task.md b/1-js/08-prototypes/01-prototype-inheritance/3-proto-and-this/task.md
index ed8482c0..b37499ba 100644
--- a/1-js/08-prototypes/01-prototype-inheritance/3-proto-and-this/task.md
+++ b/1-js/08-prototypes/01-prototype-inheritance/3-proto-and-this/task.md
@@ -2,7 +2,7 @@ importance: 5
---
-# Where does it write?
+# Where it writes?
We have `rabbit` inheriting from `animal`.
diff --git a/1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/task.md b/1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/task.md
index 50171123..6f9fb279 100644
--- a/1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/task.md
+++ b/1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/task.md
@@ -2,11 +2,11 @@ importance: 5
---
-# Why are both hamsters full?
+# Why two hamsters are full?
We have two hamsters: `speedy` and `lazy` inheriting from the general `hamster` object.
-When we feed one of them, the other one is also full. Why? How can we fix it?
+When we feed one of them, the other one is also full. Why? How to fix it?
```js run
let hamster = {
diff --git a/1-js/08-prototypes/01-prototype-inheritance/article.md b/1-js/08-prototypes/01-prototype-inheritance/article.md
index ef6c7ffe..8da5fb76 100644
--- a/1-js/08-prototypes/01-prototype-inheritance/article.md
+++ b/1-js/08-prototypes/01-prototype-inheritance/article.md
@@ -12,11 +12,11 @@ In JavaScript, objects have a special hidden property `[[Prototype]]` (as named

-When we read a property from `object`, and it's missing, JavaScript automatically takes it from the prototype. In programming, this is called "prototypal inheritance". And soon we'll study many examples of such inheritance, as well as cooler language features built upon it.
+The prototype is a little bit "magical". When we want to read a property from `object`, and it's missing, JavaScript automatically takes it from the prototype. In programming, such thing is called "prototypal inheritance". Many cool language features and programming techniques are based on it.
The property `[[Prototype]]` is internal and hidden, but there are many ways to set it.
-One of them is to use the special name `__proto__`, like this:
+One of them is to use `__proto__`, like this:
```js run
let animal = {
@@ -27,11 +27,19 @@ let rabbit = {
};
*!*
-rabbit.__proto__ = animal; // sets rabbit.[[Prototype]] = animal
+rabbit.__proto__ = animal;
*/!*
```
-Now if we read a property from `rabbit`, and it's missing, JavaScript will automatically take it from `animal`.
+```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`.
For instance:
@@ -54,7 +62,7 @@ alert( rabbit.eats ); // true (**)
alert( rabbit.jumps ); // true
```
-Here the line `(*)` sets `animal` to be the prototype of `rabbit`.
+Here the line `(*)` sets `animal` to be a prototype of `rabbit`.
Then, when `alert` tries to read property `rabbit.eats` `(**)`, it's not in `rabbit`, so JavaScript follows the `[[Prototype]]` reference and finds it in `animal` (look from the bottom up):
@@ -122,8 +130,6 @@ alert(longEar.jumps); // true (from rabbit)

-Now if we read something from `longEar`, and it's missing, JavaScript will look for it in `rabbit`, and then in `animal`.
-
There are 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.
@@ -131,18 +137,6 @@ There are only two limitations:
Also it may be obvious, but still: there can be only one `[[Prototype]]`. An object may not inherit from two others.
-```smart header="`__proto__` is a historical getter/setter for `[[Prototype]]`"
-It's a common mistake of novice developers not to know the difference between these two.
-
-Please note that `__proto__` is *not the same* as the internal `[[Prototype]]` property. It's a getter/setter for `[[Prototype]]`. Later we'll see situations where it matters, for now let's just keep it in mind, as we build our understanding of JavaScript language.
-
-The `__proto__` property is a bit outdated. It exists for historical reasons, modern JavaScript suggests that we should use `Object.getPrototypeOf/Object.setPrototypeOf` functions instead that get/set the prototype. We'll also cover these functions later.
-
-By the specification, `__proto__` must only be supported by browsers. In fact though, all environments including server-side support `__proto__`, so we're quite safe using it.
-
-As the `__proto__` notation is a bit more intuitively obvious, we use it in the examples.
-```
-
## Writing doesn't use prototype
The prototype is only used for reading properties.
@@ -203,16 +197,13 @@ alert(admin.fullName); // John Smith (*)
// setter triggers!
admin.fullName = "Alice Cooper"; // (**)
-
-alert(admin.fullName); // Alice Cooper, state of admin modified
-alert(user.fullName); // John Smith, state of user protected
```
Here in the line `(*)` the property `admin.fullName` has a getter in the prototype `user`, so it is called. And in the line `(**)` the property has a setter in the prototype, so it is called.
## The value of "this"
-An interesting question may arise in the example above: what's the value of `this` inside `set fullName(value)`? Where are the properties `this.name` and `this.surname` written: into `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.
@@ -255,13 +246,13 @@ The resulting picture:

-If we had other objects, like `bird`, `snake`, etc., inheriting from `animal`, they would also gain access to methods of `animal`. But `this` in each method call would be the corresponding object, evaluated at the call-time (before dot), not `animal`. So when we write data into `this`, it is stored into these objects.
+If we had other objects like `bird`, `snake` etc inheriting from `animal`, they would also gain access to methods of `animal`. But `this` in each method call would be the corresponding object, evaluated at the call-time (before dot), not `animal`. So when we write data into `this`, it is stored into these objects.
As a result, methods are shared, but the object state is not.
## for..in loop
-The `for..in` loop iterates over inherited properties too.
+The `for..in` loops over inherited properties too.
For instance:
@@ -276,7 +267,7 @@ let rabbit = {
};
*!*
-// Object.keys only returns own keys
+// Object.keys only return own keys
alert(Object.keys(rabbit)); // jumps
*/!*
@@ -286,7 +277,7 @@ for(let prop in rabbit) alert(prop); // jumps, then eats
*/!*
```
-If that's not what we want, and we'd like to exclude inherited properties, there's a built-in method [obj.hasOwnProperty(key)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty): it returns `true` if `obj` has its own (not inherited) property named `key`.
+If that's not 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):
diff --git a/1-js/08-prototypes/01-prototype-inheritance/object-prototype-empty.svg b/1-js/08-prototypes/01-prototype-inheritance/object-prototype-empty.svg
index eb79c19f..da48a7cc 100644
--- a/1-js/08-prototypes/01-prototype-inheritance/object-prototype-empty.svg
+++ b/1-js/08-prototypes/01-prototype-inheritance/object-prototype-empty.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-chain.svg b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-chain.svg
index 4bf580ae..520bf87e 100644
--- a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-chain.svg
+++ b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-chain.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-2.svg b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-2.svg
index 838c7839..8b657357 100644
--- a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-2.svg
+++ b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-2.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-3.svg b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-3.svg
index d791e539..6e3b6f55 100644
--- a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-3.svg
+++ b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-3.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk.svg b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk.svg
index b3247102..b83933a8 100644
--- a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk.svg
+++ b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit.svg b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit.svg
index 4f3c1bc0..538f5afb 100644
--- a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit.svg
+++ b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-user-admin.svg b/1-js/08-prototypes/01-prototype-inheritance/proto-user-admin.svg
index bf0baf01..ed9fea4a 100644
--- a/1-js/08-prototypes/01-prototype-inheritance/proto-user-admin.svg
+++ b/1-js/08-prototypes/01-prototype-inheritance/proto-user-admin.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/08-prototypes/01-prototype-inheritance/rabbit-animal-object.svg b/1-js/08-prototypes/01-prototype-inheritance/rabbit-animal-object.svg
index 32a9858f..782a858b 100644
--- a/1-js/08-prototypes/01-prototype-inheritance/rabbit-animal-object.svg
+++ b/1-js/08-prototypes/01-prototype-inheritance/rabbit-animal-object.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/08-prototypes/02-function-prototype/1-changing-prototype/task.md b/1-js/08-prototypes/02-function-prototype/1-changing-prototype/task.md
index 2838c125..4b8522d3 100644
--- a/1-js/08-prototypes/02-function-prototype/1-changing-prototype/task.md
+++ b/1-js/08-prototypes/02-function-prototype/1-changing-prototype/task.md
@@ -20,7 +20,7 @@ alert( rabbit.eats ); // true
```
-1. We added one more string (emphasized). What will `alert` show now?
+1. We added one more string (emphasized), what `alert` shows now?
```js
function Rabbit() {}
@@ -54,7 +54,7 @@ alert( rabbit.eats ); // true
alert( rabbit.eats ); // ?
```
-3. And like this (replaced one line)?
+3. Like this (replaced one line)?
```js
function Rabbit() {}
diff --git a/1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/solution.md b/1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/solution.md
index 372d50dd..0073e252 100644
--- a/1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/solution.md
+++ b/1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/solution.md
@@ -38,12 +38,7 @@ Why `user2.name` is `undefined`?
Here's how `new user.constructor('Pete')` works:
1. First, it looks for `constructor` in `user`. Nothing.
-2. Then it follows the prototype chain. The prototype of `user` is `User.prototype`, and it also has no `constructor` (because we "forgot" to set it right!).
-3. Going further up the chain, `User.prototype` is a plain object, its prototype is the built-in `Object.prototype`.
-4. Finally, for the built-in `Object.prototype`, there's a built-in `Object.prototype.constructor == Object`. So it is used.
+2. Then it follows the prototype chain. The prototype of `user` is `User.prototype`, and it also has nothing.
+3. The value of `User.prototype` is a plain object `{}`, its prototype is `Object.prototype`. And there is `Object.prototype.constructor == Object`. So it is used.
-Finally, at the end, we have `let user2 = new Object('Pete')`.
-
-Probably, that's not what we want. We'd like to create `new User`, not `new Object`. That's the outcome of the missing `constructor`.
-
-(Just in case you're curious, the `new Object(...)` call converts its argument to an object. That's a theoretical thing, in practice no one calls `new Object` with a value, and generally we don't use `new Object` to make objects at all).
\ No newline at end of file
+At the end, we have `let user2 = new Object('Pete')`. The built-in `Object` constructor ignores arguments, it always creates an empty object, similar to `let user2 = {}`, that's what we have in `user2` after all.
diff --git a/1-js/08-prototypes/02-function-prototype/article.md b/1-js/08-prototypes/02-function-prototype/article.md
index b1ef5182..29b3773e 100644
--- a/1-js/08-prototypes/02-function-prototype/article.md
+++ b/1-js/08-prototypes/02-function-prototype/article.md
@@ -2,7 +2,7 @@
Remember, new objects can be created with a constructor function, like `new F()`.
-If `F.prototype` is an object, then the `new` operator uses it to set `[[Prototype]]` for the new object.
+If `F.prototype` is an object, then `new` operator uses it to set `[[Prototype]]` for the new object.
```smart
JavaScript had prototypal inheritance from the beginning. It was one of the core features of the language.
@@ -41,7 +41,7 @@ That's the resulting picture:
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` property is only used when `new F` is called, it assigns `[[Prototype]]` of the new object.
+`F.prototype` property 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".
If, after the creation, `F.prototype` property changes (`F.prototype = `), then new objects created by `new F` will have another object as `[[Prototype]]`, but already existing objects keep the old one.
```
@@ -158,9 +158,9 @@ Rabbit.prototype = {
In this chapter we briefly described the way of setting a `[[Prototype]]` for objects created via a constructor function. Later we'll see more advanced programming patterns that rely on it.
-Everything is quite simple, just a few notes to make things clear:
+Everything is quite simple, just few notes to make things clear:
-- The `F.prototype` property (don't mistake it for `[[Prototype]]`) sets `[[Prototype]]` of new objects when `new F()` is called.
+- The `F.prototype` property (don't mess with `[[Prototype]]`) sets `[[Prototype]]` of new objects when `new F()` is called.
- The value of `F.prototype` should be either an object or `null`: other values won't work.
- The `"prototype"` property only has such a special effect when set on a constructor function, and invoked with `new`.
diff --git a/1-js/08-prototypes/02-function-prototype/function-prototype-constructor.svg b/1-js/08-prototypes/02-function-prototype/function-prototype-constructor.svg
index 59d60b39..187b899e 100644
--- a/1-js/08-prototypes/02-function-prototype/function-prototype-constructor.svg
+++ b/1-js/08-prototypes/02-function-prototype/function-prototype-constructor.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/08-prototypes/02-function-prototype/proto-constructor-animal-rabbit.svg b/1-js/08-prototypes/02-function-prototype/proto-constructor-animal-rabbit.svg
index ede4e122..a2c19d85 100644
--- a/1-js/08-prototypes/02-function-prototype/proto-constructor-animal-rabbit.svg
+++ b/1-js/08-prototypes/02-function-prototype/proto-constructor-animal-rabbit.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/08-prototypes/02-function-prototype/rabbit-prototype-constructor.svg b/1-js/08-prototypes/02-function-prototype/rabbit-prototype-constructor.svg
index 54b3d798..4d6b10e3 100644
--- a/1-js/08-prototypes/02-function-prototype/rabbit-prototype-constructor.svg
+++ b/1-js/08-prototypes/02-function-prototype/rabbit-prototype-constructor.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/08-prototypes/03-native-prototypes/article.md b/1-js/08-prototypes/03-native-prototypes/article.md
index bdfc86dd..b50e779e 100644
--- a/1-js/08-prototypes/03-native-prototypes/article.md
+++ b/1-js/08-prototypes/03-native-prototypes/article.md
@@ -2,7 +2,7 @@
The `"prototype"` property is widely used by the core of JavaScript itself. All built-in constructor functions use it.
-First we'll look at the details, and then how to use it for adding new capabilities to built-in objects.
+First we'll see at the details, and then how to use it for adding new capabilities to built-in objects.
## Object.prototype
@@ -33,9 +33,7 @@ We can check it like this:
let obj = {};
alert(obj.__proto__ === Object.prototype); // true
-
-alert(obj.toString === obj.__proto__.toString); //true
-alert(obj.toString === Object.prototype.toString); //true
+// obj.toString === obj.__proto__.toString == Object.prototype.toString
```
Please note that there is no more `[[Prototype]]` in the chain above `Object.prototype`:
@@ -101,12 +99,12 @@ alert(f.__proto__.__proto__ == Object.prototype); // true, inherit from objects
The most intricate thing happens with strings, numbers and booleans.
-As we remember, they are not objects. But if we try to access their properties, temporary wrapper objects are created using built-in constructors `String`, `Number` and `Boolean`. They provide the methods and disappear.
+As we remember, they are not objects. But if we try to access their properties, then temporary wrapper objects are created using built-in constructors `String`, `Number`, `Boolean`, they provide the methods and disappear.
These objects are created invisibly to us and most engines optimize them out, but the specification describes it exactly this way. Methods of these objects also reside in prototypes, available as `String.prototype`, `Number.prototype` and `Boolean.prototype`.
```warn header="Values `null` and `undefined` have no object wrappers"
-Special values `null` and `undefined` stand apart. They have no object wrappers, so methods and properties are not available for them. And there are no corresponding prototypes either.
+Special values `null` and `undefined` stand apart. They have no object wrappers, so methods and properties are not available for them. And there are no corresponding prototypes too.
```
## Changing native prototypes [#native-prototype-change]
@@ -131,9 +129,9 @@ So, generally, modifying a native prototype is considered a bad idea.
**In modern programming, there is only one case where modifying native prototypes is approved. That's polyfilling.**
-Polyfilling is a term for making a substitute for a method that exists in the JavaScript specification, but is not yet supported by a particular JavaScript engine.
+Polyfilling is a term for making a substitute for a method that exists in JavaScript specification, but is not yet supported by current JavaScript engine.
-We may then implement it manually and populate the built-in prototype with it.
+Then we may implement it manually and populate the built-in prototype with it.
For instance:
@@ -146,7 +144,7 @@ if (!String.prototype.repeat) { // if there's no such method
// 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
+ // but even an imperfect polyfill is often considered good enough for use
return new Array(n + 1).join(this);
};
}
@@ -181,18 +179,18 @@ obj.join = Array.prototype.join;
alert( obj.join(',') ); // Hello,world!
```
-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 if the object is indeed an array. Many built-in methods are like that.
+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.
Another possibility is to inherit by setting `obj.__proto__` to `Array.prototype`, so all `Array` methods are automatically available in `obj`.
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 functionalities from different objects if needed.
+Borrowing methods is flexible, it allows to mix functionality from different objects if needed.
## Summary
- All built-in objects follow the same pattern:
- - The methods are stored in the prototype (`Array.prototype`, `Object.prototype`, `Date.prototype`, etc.)
- - The object itself stores only the data (array items, object properties, the date)
-- Primitives also store methods in prototypes of wrapper objects: `Number.prototype`, `String.prototype` and `Boolean.prototype`. Only `undefined` and `null` do not have wrapper objects
-- Built-in prototypes can be modified or populated with new methods. But it's not recommended to change them. The only allowable case is probably when we add-in a new standard, but it's not yet supported by the JavaScript engine
+ - The methods are stored in the prototype (`Array.prototype`, `Object.prototype`, `Date.prototype` etc).
+ - The object itself stores only the data (array items, object properties, the date).
+- Primitives also store methods in prototypes of wrapper objects: `Number.prototype`, `String.prototype`, `Boolean.prototype`. Only `undefined` and `null` do not have wrapper objects.
+- Built-in prototypes can be modified or populated with new methods. But it's not recommended to change them. Probably the only allowable case is when we add-in a new standard, but not yet supported by the engine JavaScript method.
diff --git a/1-js/08-prototypes/03-native-prototypes/function-prototype-constructor.svg b/1-js/08-prototypes/03-native-prototypes/function-prototype-constructor.svg
index 59d60b39..187b899e 100644
--- a/1-js/08-prototypes/03-native-prototypes/function-prototype-constructor.svg
+++ b/1-js/08-prototypes/03-native-prototypes/function-prototype-constructor.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/08-prototypes/03-native-prototypes/native-prototypes-array-tostring.svg b/1-js/08-prototypes/03-native-prototypes/native-prototypes-array-tostring.svg
index ebb4f320..8475560b 100644
--- a/1-js/08-prototypes/03-native-prototypes/native-prototypes-array-tostring.svg
+++ b/1-js/08-prototypes/03-native-prototypes/native-prototypes-array-tostring.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/08-prototypes/03-native-prototypes/native-prototypes-classes.svg b/1-js/08-prototypes/03-native-prototypes/native-prototypes-classes.svg
index 4d6129e0..caa5ec04 100644
--- a/1-js/08-prototypes/03-native-prototypes/native-prototypes-classes.svg
+++ b/1-js/08-prototypes/03-native-prototypes/native-prototypes-classes.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/08-prototypes/03-native-prototypes/object-prototype-1.svg b/1-js/08-prototypes/03-native-prototypes/object-prototype-1.svg
index 9630e68e..c111e072 100644
--- a/1-js/08-prototypes/03-native-prototypes/object-prototype-1.svg
+++ b/1-js/08-prototypes/03-native-prototypes/object-prototype-1.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/08-prototypes/03-native-prototypes/object-prototype-null.svg b/1-js/08-prototypes/03-native-prototypes/object-prototype-null.svg
index 9ccb3422..8b802eb4 100644
--- a/1-js/08-prototypes/03-native-prototypes/object-prototype-null.svg
+++ b/1-js/08-prototypes/03-native-prototypes/object-prototype-null.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/08-prototypes/03-native-prototypes/object-prototype.svg b/1-js/08-prototypes/03-native-prototypes/object-prototype.svg
index 024dd302..b5014f9f 100644
--- a/1-js/08-prototypes/03-native-prototypes/object-prototype.svg
+++ b/1-js/08-prototypes/03-native-prototypes/object-prototype.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/08-prototypes/03-native-prototypes/proto-constructor-animal-rabbit.svg b/1-js/08-prototypes/03-native-prototypes/proto-constructor-animal-rabbit.svg
new file mode 100644
index 00000000..a2c19d85
--- /dev/null
+++ b/1-js/08-prototypes/03-native-prototypes/proto-constructor-animal-rabbit.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/1-js/08-prototypes/03-native-prototypes/rabbit-prototype-constructor.svg b/1-js/08-prototypes/03-native-prototypes/rabbit-prototype-constructor.svg
index 54b3d798..4d6b10e3 100644
--- a/1-js/08-prototypes/03-native-prototypes/rabbit-prototype-constructor.svg
+++ b/1-js/08-prototypes/03-native-prototypes/rabbit-prototype-constructor.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/solution.md b/1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/solution.md
index f3c9cf0e..a92e1790 100644
--- a/1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/solution.md
+++ b/1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/solution.md
@@ -28,4 +28,4 @@ alert(dictionary); // "apple,__proto__"
When we create a property using a descriptor, its flags are `false` by default. So in the code above, `dictionary.toString` is non-enumerable.
-See the chapter [](info:property-descriptors) for review.
+See the the chapter [](info:property-descriptors) for review.
diff --git a/1-js/08-prototypes/04-prototype-methods/article.md b/1-js/08-prototypes/04-prototype-methods/article.md
index 71f118e1..a229483f 100644
--- a/1-js/08-prototypes/04-prototype-methods/article.md
+++ b/1-js/08-prototypes/04-prototype-methods/article.md
@@ -3,18 +3,15 @@
In the first chapter of this section, we mentioned that there are modern methods to setup a prototype.
-Setting or reading the prototype with `obj.__proto__` is considered outdated and somewhat deprecated (moved to the so-called "Annex B" of the JavaScript standard, meant for browsers only).
+The `__proto__` is considered outdated and somewhat deprecated (in browser-only part of the JavaScript standard).
-The modern methods to get/set a prototype are:
+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`.
-The only usage of `__proto__`, that's not frowned upon, is as a property when creating a new object: `{ __proto__: ... }`.
-
-Although, there's a special method for this too:
-
-- [Object.create(proto, [descriptors])](mdn:js/Object/create) -- creates an empty object with given `proto` as `[[Prototype]]` and optional property descriptors.
+These should be used instead of `__proto__`.
For instance:
@@ -25,7 +22,7 @@ let animal = {
// create a new object with animal as a prototype
*!*
-let rabbit = Object.create(animal); // same as {__proto__: animal}
+let rabbit = Object.create(animal);
*/!*
alert(rabbit.eats); // true
@@ -39,9 +36,7 @@ Object.setPrototypeOf(rabbit, {}); // change the prototype of rabbit to {}
*/!*
```
-The `Object.create` method is a bit more powerful, as it has an optional second argument: property descriptors.
-
-We can provide additional properties to the new object there, like this:
+`Object.create` has an optional second argument: property descriptors. We can provide additional properties to the new object there, like this:
```js run
let animal = {
@@ -62,39 +57,32 @@ The descriptors are in the same format as described in the chapter ...get __proto__: functionset __proto__: functionObject.prototypeObjectobj[[Prototype]]prototype
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/08-prototypes/04-prototype-methods/object-prototype-null.svg b/1-js/08-prototypes/04-prototype-methods/object-prototype-null.svg
index 9ccb3422..8b802eb4 100644
--- a/1-js/08-prototypes/04-prototype-methods/object-prototype-null.svg
+++ b/1-js/08-prototypes/04-prototype-methods/object-prototype-null.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/09-classes/01-class/1-rewrite-to-class/task.md b/1-js/09-classes/01-class/1-rewrite-to-class/task.md
index 4477de67..05365e41 100644
--- a/1-js/09-classes/01-class/1-rewrite-to-class/task.md
+++ b/1-js/09-classes/01-class/1-rewrite-to-class/task.md
@@ -4,6 +4,6 @@ importance: 5
# Rewrite to class
-The `Clock` class (see the sandbox) is written in functional style. Rewrite it in the "class" syntax.
+The `Clock` class is written in functional style. Rewrite it the "class" syntax.
P.S. The clock ticks in the console, open it to see.
diff --git a/1-js/09-classes/01-class/article.md b/1-js/09-classes/01-class/article.md
index 135d2492..d6c729d2 100644
--- a/1-js/09-classes/01-class/article.md
+++ b/1-js/09-classes/01-class/article.md
@@ -51,7 +51,7 @@ user.sayHi();
When `new User("John")` is called:
1. A new object is created.
-2. The `constructor` runs with the given argument and assigns it to `this.name`.
+2. The `constructor` runs with the given argument and assigns `this.name` to it.
...Then we can call object methods, such as `user.sayHi()`.
@@ -68,7 +68,7 @@ So, what exactly is a `class`? That's not an entirely new language-level entity,
Let's unveil any magic and see what a class really is. That'll help in understanding many complex aspects.
-In JavaScript, a class is a kind of function.
+In JavaScript, a class is a kind of a function.
Here, take a look:
@@ -110,15 +110,15 @@ alert(typeof User); // function
alert(User === User.prototype.constructor); // true
// The methods are in User.prototype, e.g:
-alert(User.prototype.sayHi); // the code of the sayHi method
+alert(User.prototype.sayHi); // alert(this.name);
// there are exactly two methods in the prototype
alert(Object.getOwnPropertyNames(User.prototype)); // constructor, sayHi
```
-## Not just a syntactic sugar
+## Not just a syntax sugar
-Sometimes people say that `class` is a "syntactic sugar" (syntax that is designed to make things easier to read, but doesn't introduce anything new), because we could actually declare the same thing without using the `class` keyword at all:
+Sometimes people say that `class` is a "syntax sugar" (syntax that is designed to make things easier to read, but doesn't introduce anything new), because we could actually declare the same without `class` keyword at all:
```js run
// rewriting class User in pure functions
@@ -127,7 +127,7 @@ Sometimes people say that `class` is a "syntactic sugar" (syntax that is designe
function User(name) {
this.name = name;
}
-// a function prototype has "constructor" property by default,
+// any function prototype has constructor property by default,
// so we don't need to create it
// 2. Add the method to prototype
@@ -140,13 +140,13 @@ let user = new User("John");
user.sayHi();
```
-The result of this definition is about the same. So, there are indeed reasons why `class` can be considered a syntactic sugar to define a constructor together with its prototype methods.
+The result of this definition is about the same. So, there are indeed reasons why `class` can be considered a syntax sugar to define a constructor together with its prototype methods.
Still, there are important differences.
-1. First, a function created by `class` is labelled by a special internal property `[[IsClassConstructor]]: true`. So it's not entirely the same as creating it manually.
+1. First, a function created by `class` is labelled by a special internal property `[[FunctionKind]]:"classConstructor"`. So it's not entirely the same as creating it manually.
- The language checks for that property in a variety of places. For example, unlike a regular function, it must be called with `new`:
+ And unlike a regular function, a class constructor must be called with `new`:
```js run
class User {
@@ -166,7 +166,6 @@ Still, there are important differences.
alert(User); // class User { ... }
```
- There are other differences, we'll see them soon.
2. Class methods are non-enumerable.
A class definition sets `enumerable` flag to `false` for all methods in the `"prototype"`.
@@ -180,7 +179,7 @@ Besides, `class` syntax brings many other features that we'll explore later.
## Class Expression
-Just like functions, classes can be defined inside another expression, passed around, returned, assigned, etc.
+Just like functions, classes can be defined inside another expression, passed around, returned, assigned etc.
Here's an example of a class expression:
@@ -210,6 +209,7 @@ new User().sayHi(); // works, shows MyClass definition
alert(MyClass); // error, MyClass name isn't visible outside of the class
```
+
We can even make classes dynamically "on-demand", like this:
```js run
@@ -218,7 +218,7 @@ function makeClass(phrase) {
return class {
sayHi() {
alert(phrase);
- }
+ };
};
}
@@ -229,7 +229,7 @@ new User().sayHi(); // Hello
```
-## Getters/setters
+## Getters/setters, other shorthands
Just like literal objects, classes may include getters/setters, computed properties etc.
@@ -264,14 +264,25 @@ class User {
let user = new User("John");
alert(user.name); // John
-user = new User(""); // Name is too short.
+user = new User(""); // Name too short.
```
-Technically, such class declaration works by creating getters and setters in `User.prototype`.
+The class declaration creates getters and setters in `User.prototype`, like this:
-## Computed names [...]
+```js
+Object.defineProperties(User.prototype, {
+ name: {
+ get() {
+ return this._name
+ },
+ set(name) {
+ // ...
+ }
+ }
+});
+```
-Here's an example with a computed method name using brackets `[...]`:
+Here's an example with a computed property in brackets `[...]`:
```js run
class User {
@@ -287,24 +298,18 @@ class User {
new User().sayHi();
```
-Such features are easy to remember, as they resemble that of literal objects.
-
-## Class fields
+## Class properties
```warn header="Old browsers may need a polyfill"
-Class fields are a recent addition to the language.
+Class-level properties are a recent addition to the language.
```
-Previously, our classes only had methods.
-
-"Class fields" is a syntax that allows to add any properties.
-
-For instance, let's add `name` property to `class User`:
+In the example above, `User` only had methods. Let's add a property:
```js run
class User {
*!*
- name = "John";
+ name = "Anonymous";
*/!*
sayHi() {
@@ -312,94 +317,10 @@ class User {
}
}
-new User().sayHi(); // Hello, John!
+new User().sayHi();
```
-So, we just write " = " in the declaration, and that's it.
-
-The important difference of class fields is that they are set on individual objects, not `User.prototype`:
-
-```js run
-class User {
-*!*
- name = "John";
-*/!*
-}
-
-let user = new User();
-alert(user.name); // John
-alert(User.prototype.name); // undefined
-```
-
-We can also assign values using more complex expressions and function calls:
-
-```js run
-class User {
-*!*
- name = prompt("Name, please?", "John");
-*/!*
-}
-
-let user = new User();
-alert(user.name); // John
-```
-
-
-### Making bound methods with class fields
-
-As demonstrated in the chapter functions in JavaScript have a dynamic `this`. It depends on the context of the call.
-
-So if an object method is passed around and called in another context, `this` won't be a reference to its object any more.
-
-For instance, this code will show `undefined`:
-
-```js run
-class Button {
- constructor(value) {
- this.value = value;
- }
-
- click() {
- alert(this.value);
- }
-}
-
-let button = new Button("hello");
-
-*!*
-setTimeout(button.click, 1000); // undefined
-*/!*
-```
-
-The problem is called "losing `this`".
-
-There are two approaches to fixing it, as discussed in the chapter :
-
-1. Pass a wrapper-function, such as `setTimeout(() => button.click(), 1000)`.
-2. Bind the method to object, e.g. in the constructor.
-
-Class fields provide another, quite elegant syntax:
-
-```js run
-class Button {
- constructor(value) {
- this.value = value;
- }
-*!*
- click = () => {
- alert(this.value);
- }
-*/!*
-}
-
-let button = new Button("hello");
-
-setTimeout(button.click, 1000); // hello
-```
-
-The class field `click = () => {...}` is created on a per-object basis, there's a separate function for each `Button` object, with `this` inside it referencing that object. We can pass `button.click` around anywhere, and the value of `this` will always be correct.
-
-That's especially useful in browser environment, for event listeners.
+The property `name` is not placed into `User.prototype`. Instead, it is created by `new` before calling the constructor, it's a property of the object itself.
## Summary
@@ -423,6 +344,6 @@ class MyClass {
}
```
-`MyClass` is technically a function (the one that we provide as `constructor`), while methods, getters and setters are written to `MyClass.prototype`.
+`MyClass` is technically a function (the one that we provide as `constructor`), while methods, getters and settors are written to `MyClass.prototype`.
In the next chapters we'll learn more about classes, including inheritance and other features.
diff --git a/1-js/09-classes/01-class/class-user.svg b/1-js/09-classes/01-class/class-user.svg
index 418d71d1..95b58179 100644
--- a/1-js/09-classes/01-class/class-user.svg
+++ b/1-js/09-classes/01-class/class-user.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js
index be2053cf..ca613ca5 100644
--- a/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js
+++ b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js
@@ -1,7 +1,7 @@
class ExtendedClock extends Clock {
constructor(options) {
super(options);
- let { precision = 1000 } = options;
+ let { precision=1000 } = options;
this.precision = precision;
}
diff --git a/1-js/09-classes/03-static-properties-methods/3-class-extend-object/rabbit-extends-object.svg b/1-js/09-classes/02-class-inheritance/3-class-extend-object/rabbit-extends-object.svg
similarity index 59%
rename from 1-js/09-classes/03-static-properties-methods/3-class-extend-object/rabbit-extends-object.svg
rename to 1-js/09-classes/02-class-inheritance/3-class-extend-object/rabbit-extends-object.svg
index 915ab9aa..34d783b4 100644
--- a/1-js/09-classes/03-static-properties-methods/3-class-extend-object/rabbit-extends-object.svg
+++ b/1-js/09-classes/02-class-inheritance/3-class-extend-object/rabbit-extends-object.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/09-classes/03-static-properties-methods/3-class-extend-object/solution.md b/1-js/09-classes/02-class-inheritance/3-class-extend-object/solution.md
similarity index 79%
rename from 1-js/09-classes/03-static-properties-methods/3-class-extend-object/solution.md
rename to 1-js/09-classes/02-class-inheritance/3-class-extend-object/solution.md
index cb9829ce..ca9e8060 100644
--- a/1-js/09-classes/03-static-properties-methods/3-class-extend-object/solution.md
+++ b/1-js/09-classes/02-class-inheritance/3-class-extend-object/solution.md
@@ -21,14 +21,14 @@ alert( rabbit.hasOwnProperty('name') ); // true
But that's not all yet.
-Even after the fix, there's still an important difference between `"class Rabbit extends Object"` and `class Rabbit`.
+Even after the fix, there's still important difference in `"class Rabbit extends Object"` versus `class Rabbit`.
As we know, the "extends" syntax sets up two prototypes:
1. Between `"prototype"` of the constructor functions (for methods).
2. Between the constructor functions themselves (for static methods).
-In the case of `class Rabbit extends Object` it means:
+In our case, for `class Rabbit extends Object` it means:
```js run
class Rabbit extends Object {}
@@ -37,7 +37,7 @@ alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true
alert( Rabbit.__proto__ === Object ); // (2) true
```
-So `Rabbit` now provides access to the static methods of `Object` via `Rabbit`, like this:
+So `Rabbit` now provides access to static methods of `Object` via `Rabbit`, like this:
```js run
class Rabbit extends Object {}
@@ -67,7 +67,7 @@ alert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // Error
So `Rabbit` doesn't provide access to static methods of `Object` in that case.
-By the way, `Function.prototype` also has "generic" function methods, like `call`, `bind` etc. They are ultimately available in both cases, because for the built-in `Object` constructor, `Object.__proto__ === Function.prototype`.
+By the way, `Function.prototype` has "generic" function methods, like `call`, `bind` etc. They are ultimately available in both cases, because for the built-in `Object` constructor, `Object.__proto__ === Function.prototype`.
Here's the picture:
diff --git a/1-js/09-classes/03-static-properties-methods/3-class-extend-object/task.md b/1-js/09-classes/02-class-inheritance/3-class-extend-object/task.md
similarity index 92%
rename from 1-js/09-classes/03-static-properties-methods/3-class-extend-object/task.md
rename to 1-js/09-classes/02-class-inheritance/3-class-extend-object/task.md
index 1d0f98a7..b82a4255 100644
--- a/1-js/09-classes/03-static-properties-methods/3-class-extend-object/task.md
+++ b/1-js/09-classes/02-class-inheritance/3-class-extend-object/task.md
@@ -1,4 +1,4 @@
-importance: 3
+importance: 5
---
@@ -38,5 +38,5 @@ class Rabbit extends Object {
let rabbit = new Rabbit("Rab");
-alert( rabbit.hasOwnProperty('name') ); // Error
+alert( rabbit.hasOwnProperty('name') ); // true
```
diff --git a/1-js/09-classes/02-class-inheritance/animal-rabbit-extends.svg b/1-js/09-classes/02-class-inheritance/animal-rabbit-extends.svg
index 63b5a18a..2f994f7b 100644
--- a/1-js/09-classes/02-class-inheritance/animal-rabbit-extends.svg
+++ b/1-js/09-classes/02-class-inheritance/animal-rabbit-extends.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/09-classes/02-class-inheritance/article.md b/1-js/09-classes/02-class-inheritance/article.md
index 464042d8..d1685f20 100644
--- a/1-js/09-classes/02-class-inheritance/article.md
+++ b/1-js/09-classes/02-class-inheritance/article.md
@@ -7,7 +7,7 @@ So we can create new functionality on top of the existing.
## The "extends" keyword
-Let's say we have class `Animal`:
+Let's say with have class `Animal`:
```js
class Animal {
@@ -16,7 +16,7 @@ class Animal {
this.name = name;
}
run(speed) {
- this.speed = speed;
+ this.speed += speed;
alert(`${this.name} runs with speed ${this.speed}.`);
}
stop() {
@@ -55,7 +55,7 @@ rabbit.run(5); // White Rabbit runs with speed 5.
rabbit.hide(); // White Rabbit hides!
```
-Object of `Rabbit` class have access both to `Rabbit` methods, such as `rabbit.hide()`, and also to `Animal` methods, such as `rabbit.run()`.
+Object of `Rabbit` class have access to both `Rabbit` methods, such as `rabbit.hide()`, and also to `Animal` methods, such as `rabbit.run()`.
Internally, `extends` keyword works using the good old prototype mechanics. It sets `Rabbit.prototype.[[Prototype]]` to `Animal.prototype`. So, if a method is not found in `Rabbit.prototype`, JavaScript takes it from `Animal.prototype`.
@@ -66,7 +66,7 @@ For instance, to find `rabbit.run` method, the engine checks (bottom-up on the p
2. Its prototype, that is `Rabbit.prototype` (has `hide`, but not `run`).
3. Its prototype, that is (due to `extends`) `Animal.prototype`, that finally has the `run` method.
-As we can recall from the chapter , JavaScript itself uses prototypal inheritance for built-in objects. E.g. `Date.prototype.[[Prototype]]` is `Object.prototype`. That's why dates have access to generic object methods.
+As we can recall from the chapter , JavaScript itself uses prototypal inheritance for build-in objects. E.g. `Date.prototype.[[Prototype]]` is `Object.prototype`. That's why dates have access to generic object methods.
````smart header="Any expression is allowed after `extends`"
Class syntax allows to specify not just a class, but any expression after `extends`.
@@ -76,8 +76,8 @@ For instance, a function call that generates the parent class:
```js run
function f(phrase) {
return class {
- sayHi() { alert(phrase); }
- };
+ sayHi() { alert(phrase) }
+ }
}
*!*
@@ -106,7 +106,7 @@ class Rabbit extends Animal {
}
```
-Usually, however, we don't want to totally replace a parent method, but rather to build on top of it to tweak or extend its functionality. We do something in our method, but call the parent method before/after it or in the process.
+Usually we don't want to totally replace a parent method, but rather to build on top of it to tweak or extend its functionality. We do something in our method, but call the parent method before/after it or in the process.
Classes provide `"super"` keyword for that.
@@ -124,7 +124,7 @@ class Animal {
}
run(speed) {
- this.speed = speed;
+ this.speed += speed;
alert(`${this.name} runs with speed ${this.speed}.`);
}
@@ -151,7 +151,7 @@ class Rabbit extends Animal {
let rabbit = new Rabbit("White Rabbit");
rabbit.run(5); // White Rabbit runs with speed 5.
-rabbit.stop(); // White Rabbit stands still. White Rabbit hides!
+rabbit.stop(); // White Rabbit stands still. White rabbit hides!
```
Now `Rabbit` has the `stop` method that calls the parent `super.stop()` in the process.
@@ -160,7 +160,6 @@ Now `Rabbit` has the `stop` method that calls the parent `super.stop()` in the p
As was mentioned in the chapter , arrow functions do not have `super`.
If accessed, it's taken from the outer function. For instance:
-
```js
class Rabbit extends Animal {
stop() {
@@ -177,11 +176,12 @@ setTimeout(function() { super.stop() }, 1000);
```
````
+
## Overriding constructor
With constructors it gets a little bit tricky.
-Until now, `Rabbit` did not have its own `constructor`.
+Till now, `Rabbit` did not have its own `constructor`.
According to the [specification](https://tc39.github.io/ecma262/#sec-runtime-semantics-classdefinitionevaluation), if a class extends another class and has no `constructor`, then the following "empty" `constructor` is generated:
@@ -230,24 +230,22 @@ let rabbit = new Rabbit("White Rabbit", 10); // Error: this is not defined.
Whoops! We've got an error. Now we can't create rabbits. What went wrong?
-The short answer is:
-
-- **Constructors in inheriting classes must call `super(...)`, and (!) do it before using `this`.**
+The short answer is: constructors in inheriting classes must call `super(...)`, and (!) do it before using `this`.
...But why? What's going on here? Indeed, the requirement seems strange.
Of course, there's an explanation. Let's get into details, so you'll really understand what's going on.
-In JavaScript, there's a distinction between a constructor function of an inheriting class (so-called "derived constructor") and other functions. A derived constructor has a special internal property `[[ConstructorKind]]:"derived"`. That's a special internal label.
+In JavaScript, there's a distinction between a "constructor function of an inheriting class" and all others. In an inheriting class, the corresponding constructor function is labeled with a special internal property `[[ConstructorKind]]:"derived"`.
-That label affects its behavior with `new`.
+The difference is:
-- When a regular function is executed with `new`, it creates an empty object and assigns it to `this`.
+- When a normal constructor runs, it creates an empty object and assigns it to `this`.
- But when a derived constructor runs, it doesn't do this. It expects the parent constructor to do this job.
-So a derived constructor must call `super` in order to execute its parent (base) constructor, otherwise the object for `this` won't be created. And we'll get an error.
+So if we're making a constructor of our own, then we must call `super`, because otherwise the object for `this` won't be created. And we'll get an error.
-For the `Rabbit` constructor to work, it needs to call `super()` before using `this`, like here:
+For `Rabbit` constructor to work, it needs to call `super()` before using `this`, like here:
```js run
class Animal {
@@ -280,99 +278,6 @@ alert(rabbit.earLength); // 10
*/!*
```
-### Overriding class fields: a tricky note
-
-```warn header="Advanced note"
-This note assumes you have a certain experience with classes, maybe in other programming languages.
-
-It provides better insight into the language and also explains the behavior that might be a source of bugs (but not very often).
-
-If you find it difficult to understand, just go on, continue reading, then return to it some time later.
-```
-
-We can override not only methods, but also class fields.
-
-Although, there's a tricky behavior when we access an overridden field in parent constructor, quite different from most other programming languages.
-
-Consider this example:
-
-```js run
-class Animal {
- name = 'animal';
-
- constructor() {
- alert(this.name); // (*)
- }
-}
-
-class Rabbit extends Animal {
- name = 'rabbit';
-}
-
-new Animal(); // animal
-*!*
-new Rabbit(); // animal
-*/!*
-```
-
-Here, class `Rabbit` extends `Animal` and overrides the `name` field with its own value.
-
-There's no own constructor in `Rabbit`, so `Animal` constructor is called.
-
-What's interesting is that in both cases: `new Animal()` and `new Rabbit()`, the `alert` in the line `(*)` shows `animal`.
-
-**In other words, the parent constructor always uses its own field value, not the overridden one.**
-
-What's odd about it?
-
-If it's not clear yet, please compare with methods.
-
-Here's the same code, but instead of `this.name` field we call `this.showName()` method:
-
-```js run
-class Animal {
- showName() { // instead of this.name = 'animal'
- alert('animal');
- }
-
- constructor() {
- this.showName(); // instead of alert(this.name);
- }
-}
-
-class Rabbit extends Animal {
- showName() {
- alert('rabbit');
- }
-}
-
-new Animal(); // animal
-*!*
-new Rabbit(); // rabbit
-*/!*
-```
-
-Please note: now the output is different.
-
-And that's what we naturally expect. When the parent constructor is called in the derived class, it uses the overridden method.
-
-...But for class fields it's not so. As said, the parent constructor always uses the parent field.
-
-Why is there a difference?
-
-Well, the reason is the field initialization order. The class field is initialized:
-- Before constructor for the base class (that doesn't extend anything),
-- Immediately after `super()` for the derived class.
-
-In our case, `Rabbit` is the derived class. There's no `constructor()` in it. As said previously, that's the same as if there was an empty constructor with only `super(...args)`.
-
-So, `new Rabbit()` calls `super()`, thus executing the parent constructor, and (per the rule for derived classes) only after that its class fields are initialized. At the time of the parent constructor execution, there are no `Rabbit` class fields yet, that's why `Animal` fields are used.
-
-This subtle difference between fields and methods is specific to JavaScript.
-
-Luckily, this behavior only reveals itself if an overridden field is used in the parent constructor. Then it may be difficult to understand what's going on, so we're explaining it here.
-
-If it becomes a problem, one can fix it by using methods or getters/setters instead of fields.
## Super: internals, [[HomeObject]]
@@ -533,7 +438,7 @@ It works as intended, due to `[[HomeObject]]` mechanics. A method, such as `long
As we've known before, generally functions are "free", not bound to objects in JavaScript. So they can be copied between objects and called with another `this`.
-The very existence of `[[HomeObject]]` violates that principle, because methods remember their objects. `[[HomeObject]]` can't be changed, so this bond is forever.
+The very existance of `[[HomeObject]]` violates that principle, because methods remember their objects. `[[HomeObject]]` can't be changed, so this bond is forever.
The only place in the language where `[[HomeObject]]` is used -- is `super`. So, if a method does not use `super`, then we can still consider it free and copy between objects. But with `super` things may go wrong.
@@ -542,7 +447,7 @@ Here's the demo of a wrong `super` result after copying:
```js run
let animal = {
sayHi() {
- alert(`I'm an animal`);
+ console.log(`I'm an animal`);
}
};
@@ -556,7 +461,7 @@ let rabbit = {
let plant = {
sayHi() {
- alert("I'm a plant");
+ console.log("I'm a plant");
}
};
@@ -573,7 +478,7 @@ tree.sayHi(); // I'm an animal (?!?)
*/!*
```
-A call to `tree.sayHi()` shows "I'm an animal". Definitely wrong.
+A call to `tree.sayHi()` shows "I'm an animal". Definitevely wrong.
The reason is simple:
- In the line `(*)`, the method `tree.sayHi` was copied from `rabbit`. Maybe we just wanted to avoid code duplication?
@@ -594,7 +499,7 @@ In the example below a non-method syntax is used for comparison. `[[HomeObject]]
```js run
let animal = {
- eat: function() { // intentionally writing like this instead of eat() {...
+ eat: function() { // intentially writing like this instead of eat() {...
// ...
}
};
@@ -624,4 +529,4 @@ rabbit.eat(); // Error calling super (because there's no [[HomeObject]])
- So it's not safe to copy a method with `super` from one object to another.
Also:
-- Arrow functions don't have their own `this` or `super`, so they transparently fit into the surrounding context.
+- Arrow functions don't have own `this` or `super`, so they transparently fit into the surrounding context.
diff --git a/1-js/09-classes/02-class-inheritance/class-inheritance-array-object.svg b/1-js/09-classes/02-class-inheritance/class-inheritance-array-object.svg
index 5ea9bf29..10af6c4c 100644
--- a/1-js/09-classes/02-class-inheritance/class-inheritance-array-object.svg
+++ b/1-js/09-classes/02-class-inheritance/class-inheritance-array-object.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal-2.svg b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal-2.svg
index 72e47e34..a81676e2 100644
--- a/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal-2.svg
+++ b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal-2.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal.svg b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal.svg
index bced3d35..35529aa4 100644
--- a/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal.svg
+++ b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-animal.svg b/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-animal.svg
index f53fc92d..905efe37 100644
--- a/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-animal.svg
+++ b/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-animal.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-rabbit.svg b/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-rabbit.svg
index 2f30a3a9..81bf1850 100644
--- a/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-rabbit.svg
+++ b/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-rabbit.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/09-classes/02-class-inheritance/super-homeobject-wrong.svg b/1-js/09-classes/02-class-inheritance/super-homeobject-wrong.svg
index f6450ddc..8ad09f48 100644
--- a/1-js/09-classes/02-class-inheritance/super-homeobject-wrong.svg
+++ b/1-js/09-classes/02-class-inheritance/super-homeobject-wrong.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/09-classes/02-class-inheritance/this-super-loop.svg b/1-js/09-classes/02-class-inheritance/this-super-loop.svg
index 4f5f4503..bc200fab 100644
--- a/1-js/09-classes/02-class-inheritance/this-super-loop.svg
+++ b/1-js/09-classes/02-class-inheritance/this-super-loop.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/09-classes/03-static-properties-methods/animal-rabbit-static.svg b/1-js/09-classes/03-static-properties-methods/animal-rabbit-static.svg
index 3e354b89..18093d7c 100644
--- a/1-js/09-classes/03-static-properties-methods/animal-rabbit-static.svg
+++ b/1-js/09-classes/03-static-properties-methods/animal-rabbit-static.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/09-classes/03-static-properties-methods/article.md b/1-js/09-classes/03-static-properties-methods/article.md
index 4b493a5e..8e08514f 100644
--- a/1-js/09-classes/03-static-properties-methods/article.md
+++ b/1-js/09-classes/03-static-properties-methods/article.md
@@ -1,9 +1,9 @@
# Static properties and methods
-We can also assign a method to the class as a whole. Such methods are called *static*.
+We can also assign a method to the class function itself, not to its `"prototype"`. Such methods are called *static*.
-In a class declaration, they are prepended by `static` keyword, like this:
+In a class, they are prepended by `static` keyword, like this:
```js run
class User {
@@ -19,23 +19,19 @@ User.staticMethod(); // true
That actually does the same as assigning it as a property directly:
-```js run
-class User { }
+```js
+class User() { }
User.staticMethod = function() {
alert(this === User);
};
-
-User.staticMethod(); // true
```
The value of `this` in `User.staticMethod()` call is the class constructor `User` itself (the "object before dot" rule).
-Usually, static methods are used to implement functions that belong to the class as a whole, but not to any particular object of it.
+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.
-
-A natural solution would be to add `Article.compare` static method:
+For instance, we have `Article` objects and need a function to compare them. A natural solution would be to add `Article.compare` method, like this:
```js run
class Article {
@@ -65,11 +61,9 @@ articles.sort(Article.compare);
alert( articles[0].title ); // CSS
```
-Here `Article.compare` method stands "above" articles, as a means to compare them. It's not a method of an article, but rather of the whole class.
+Here `Article.compare` stands "above" 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.
-
-Let's say, we need multiple ways to create an article:
+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.
@@ -77,7 +71,7 @@ Let's say, we need multiple ways to create an article:
The first way can be implemented by the constructor. And for the second one we can make a static method of the class.
-Such as `Article.createTodays()` here:
+Like `Article.createTodays()` here:
```js run
class Article {
@@ -105,21 +99,10 @@ Static methods are also used in database-related classes to search/save/remove e
```js
// assuming Article is a special class for managing articles
-// static method to remove the article by id:
+// static method to remove the article:
Article.remove({id: 12345});
```
-````warn header="Static methods aren't available for individual objects"
-Static methods are callable on classes, not on individual objects.
-
-E.g. such code won't work:
-
-```js
-// ...
-article.createTodays(); /// Error: article.createTodays is not a function
-```
-````
-
## Static properties
[recent browser=Chrome]
@@ -140,15 +123,14 @@ That is the same as a direct assignment to `Article`:
Article.publisher = "Ilya Kantor";
```
-## Inheritance of static properties and methods [#statics-and-inheritance]
+## Inheritance of static methods
-Static properties and methods are inherited.
+Static methods are inherited.
-For instance, `Animal.compare` and `Animal.planet` in the code below are inherited and accessible as `Rabbit.compare` and `Rabbit.planet`:
+For instance, `Animal.compare` in the code below is inherited and accessible as `Rabbit.compare`:
```js run
class Animal {
- static planet = "Earth";
constructor(name, speed) {
this.speed = speed;
@@ -185,11 +167,9 @@ rabbits.sort(Rabbit.compare);
*/!*
rabbits[0].run(); // Black Rabbit runs with speed 5.
-
-alert(Rabbit.planet); // Earth
```
-Now when we call `Rabbit.compare`, the inherited `Animal.compare` will be called.
+Now when we can call `Rabbit.compare`, the inherited `Animal.compare` will be called.
How does it work? Again, using prototypes. As you might have already guessed, `extends` gives `Rabbit` the `[[Prototype]]` reference to `Animal`.
@@ -200,7 +180,7 @@ So, `Rabbit extends Animal` creates two `[[Prototype]]` references:
1. `Rabbit` function prototypally inherits from `Animal` function.
2. `Rabbit.prototype` prototypally inherits from `Animal.prototype`.
-As a result, inheritance works both for regular and static methods.
+As the result, inheritance works both for regular and static methods.
Here, let's check that by code:
@@ -217,7 +197,7 @@ alert(Rabbit.prototype.__proto__ === Animal.prototype); // true
## Summary
-Static methods are used for the functionality that belongs to the class "as a whole". It doesn't relate to a concrete class instance.
+Static methods are used for the functionality that belongs to the class "as a whole", doesn't relate to a concrete class instance.
For example, a method for comparison `Article.compare(article1, article2)` or a factory method `Article.createTodays()`.
diff --git a/1-js/09-classes/04-private-protected-properties-methods/article.md b/1-js/09-classes/04-private-protected-properties-methods/article.md
index 91efb89e..fb1a964c 100644
--- a/1-js/09-classes/04-private-protected-properties-methods/article.md
+++ b/1-js/09-classes/04-private-protected-properties-methods/article.md
@@ -50,7 +50,7 @@ That was a general introduction.
In JavaScript, there are two types of object fields (properties and methods):
-- Public: accessible from anywhere. They comprise the external interface. Until now we were only using public properties and methods.
+- 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 (like private, but plus access from inheriting classes). 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 them.
@@ -96,9 +96,7 @@ class CoffeeMachine {
_waterAmount = 0;
set waterAmount(value) {
- if (value < 0) {
- value = 0;
- }
+ if (value < 0) throw new Error("Negative water");
this._waterAmount = value;
}
@@ -116,10 +114,10 @@ class CoffeeMachine {
let coffeeMachine = new CoffeeMachine(100);
// add water
-coffeeMachine.waterAmount = -10; // _waterAmount will become 0, not -10
+coffeeMachine.waterAmount = -10; // Error: Negative water
```
-Now the access is under control, so setting the water amount below zero becomes impossible.
+Now the access is under control, so setting the water below zero fails.
## Read-only "power"
@@ -161,7 +159,7 @@ class CoffeeMachine {
_waterAmount = 0;
*!*setWaterAmount(value)*/!* {
- if (value < 0) value = 0;
+ if (value < 0) throw new Error("Negative water");
this._waterAmount = value;
}
@@ -192,7 +190,7 @@ There's a finished JavaScript proposal, almost in the standard, that provides la
Privates should start with `#`. They are only accessible from inside the class.
-For instance, here's a private `#waterLimit` property and the water-checking private method `#fixWaterAmount`:
+For instance, here's a private `#waterLimit` property and the water-checking private method `#checkWater`:
```js run
class CoffeeMachine {
@@ -201,23 +199,19 @@ class CoffeeMachine {
*/!*
*!*
- #fixWaterAmount(value) {
- if (value < 0) return 0;
- if (value > this.#waterLimit) return this.#waterLimit;
+ #checkWater(value) {
+ if (value < 0) throw new Error("Negative water");
+ if (value > this.#waterLimit) throw new Error("Too much water");
}
*/!*
- setWaterAmount(value) {
- this.#waterLimit = this.#fixWaterAmount(value);
- }
-
}
let coffeeMachine = new CoffeeMachine();
*!*
// can't access privates from outside of the class
-coffeeMachine.#fixWaterAmount(123); // Error
+coffeeMachine.#checkWater(); // Error
coffeeMachine.#waterLimit = 1000; // Error
*/!*
```
@@ -238,7 +232,7 @@ class CoffeeMachine {
}
set waterAmount(value) {
- if (value < 0) value = 0;
+ if (value < 0) throw new Error("Negative water");
this.#waterAmount = value;
}
}
@@ -263,7 +257,7 @@ class MegaCoffeeMachine extends CoffeeMachine {
}
```
-In many scenarios such limitation is too severe. If we extend a `CoffeeMachine`, we may have legitimate reasons to access its internals. That's why protected fields are used more often, even though they are not supported by the language syntax.
+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 more often, even though they are not supported by the language syntax.
````warn header="Private fields are not available as this[name]"
Private fields are special.
@@ -285,11 +279,11 @@ With private fields that's impossible: `this['#name']` doesn't work. That's a sy
## 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)).
+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 foot
+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.
@@ -314,9 +308,9 @@ Hiding complexity
**It's always convenient when implementation details are hidden, and a simple, well-documented external interface is available.**
-To hide an internal interface we use either protected or private properties:
+To hide internal interface we use either protected or private 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 can only access those from inside the class.
+- 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-extend-natives/article.md b/1-js/09-classes/05-extend-natives/article.md
index 28b4c6eb..9a568673 100644
--- a/1-js/09-classes/05-extend-natives/article.md
+++ b/1-js/09-classes/05-extend-natives/article.md
@@ -32,7 +32,7 @@ When `arr.filter()` is called, it internally creates the new array of results us
Even more, we can customize that behavior.
-We can add a special static getter `Symbol.species` to the class. If it exists, it should return the constructor that JavaScript will use internally to create new entities in `map`, `filter` and so on.
+We can add a special static getter `Symbol.species` to the class. If exists, it should return the constructor that JavaScript will use internally to create new entities in `map`, `filter` and so on.
If we'd like built-in methods like `map` or `filter` to return regular arrays, we can return `Array` in `Symbol.species`, like here:
diff --git a/1-js/09-classes/05-extend-natives/object-date-inheritance.svg b/1-js/09-classes/05-extend-natives/object-date-inheritance.svg
index be47d7fd..470aabf7 100644
--- a/1-js/09-classes/05-extend-natives/object-date-inheritance.svg
+++ b/1-js/09-classes/05-extend-natives/object-date-inheritance.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/09-classes/06-instanceof/1-strange-instanceof/task.md b/1-js/09-classes/06-instanceof/1-strange-instanceof/task.md
index 5b8dc7de..e9481912 100644
--- a/1-js/09-classes/06-instanceof/1-strange-instanceof/task.md
+++ b/1-js/09-classes/06-instanceof/1-strange-instanceof/task.md
@@ -4,7 +4,7 @@ importance: 5
# Strange instanceof
-In the code below, why does `instanceof` return `true`? We can easily see that `a` is not created by `B()`.
+Why `instanceof` below returns `true`? We can easily see that `a` is not created by `B()`.
```js run
function A() {}
diff --git a/1-js/09-classes/06-instanceof/article.md b/1-js/09-classes/06-instanceof/article.md
index f9db989c..544e07eb 100644
--- a/1-js/09-classes/06-instanceof/article.md
+++ b/1-js/09-classes/06-instanceof/article.md
@@ -2,7 +2,7 @@
The `instanceof` operator allows to check whether an object belongs to a certain class. It also takes inheritance into account.
-Such a check may be necessary in many cases. For example, it can be used for building a *polymorphic* function, the one that treats arguments differently depending on their type.
+Such a check may be necessary in many cases, here we'll use it for building a *polymorphic* function, the one that treats arguments differently depending on their type.
## The instanceof operator [#ref-instanceof]
@@ -46,7 +46,7 @@ alert( arr instanceof Object ); // true
Please note that `arr` also belongs to the `Object` class. That's because `Array` prototypically inherits from `Object`.
-Normally, `instanceof` examines the prototype chain for the check. We can also set a custom logic in the static method `Symbol.hasInstance`.
+Normally, `instanceof` operator examines the prototype chain for the check. We can also set a custom logic in the static method `Symbol.hasInstance`.
The algorithm of `obj instanceof Class` works roughly as follows:
@@ -68,7 +68,7 @@ The algorithm of `obj instanceof Class` works roughly as follows:
alert(obj instanceof Animal); // true: Animal[Symbol.hasInstance](obj) is called
```
-2. Most classes do not have `Symbol.hasInstance`. In that case, the standard logic is used: `obj instanceOf Class` checks whether `Class.prototype` is equal to one of the prototypes in the `obj` prototype chain.
+2. Most classes do not have `Symbol.hasInstance`. In that case, the standard logic is used: `obj instanceOf Class` checks whether `Class.prototype` equals to one of prototypes in the `obj` prototype chain.
In other words, compare one after another:
```js
@@ -93,7 +93,7 @@ The algorithm of `obj instanceof Class` works roughly as follows:
alert(rabbit instanceof Animal); // true
*/!*
- // rabbit.__proto__ === Animal.prototype (no match)
+ // rabbit.__proto__ === Rabbit.prototype
*!*
// rabbit.__proto__.__proto__ === Animal.prototype (match!)
*/!*
@@ -107,7 +107,7 @@ By the way, there's also a method [objA.isPrototypeOf(objB)](mdn:js/object/isPro
It's funny, but the `Class` constructor itself does not participate in the check! Only the chain of prototypes and `Class.prototype` matters.
-That can lead to interesting consequences when a `prototype` property is changed after the object is created.
+That can lead to interesting consequences when `prototype` property is changed after the object is created.
Like here:
@@ -186,11 +186,11 @@ let user = {
alert( {}.toString.call(user) ); // [object User]
```
-For most environment-specific objects, there is such a property. Here are some browser specific examples:
+For most environment-specific objects, there is such a property. Here are few browser specific examples:
```js run
// toStringTag for the environment-specific object and class:
-alert( window[Symbol.toStringTag]); // Window
+alert( window[Symbol.toStringTag]); // window
alert( XMLHttpRequest.prototype[Symbol.toStringTag] ); // XMLHttpRequest
alert( {}.toString.call(window) ); // [object Window]
diff --git a/1-js/09-classes/06-instanceof/instanceof.svg b/1-js/09-classes/06-instanceof/instanceof.svg
index d63b03a8..8b63f207 100644
--- a/1-js/09-classes/06-instanceof/instanceof.svg
+++ b/1-js/09-classes/06-instanceof/instanceof.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/09-classes/07-mixins/article.md b/1-js/09-classes/07-mixins/article.md
index 526b832e..3be78c62 100644
--- a/1-js/09-classes/07-mixins/article.md
+++ b/1-js/09-classes/07-mixins/article.md
@@ -69,7 +69,7 @@ let sayMixin = {
};
let sayHiMixin = {
- __proto__: sayMixin, // (or we could use Object.setPrototypeOf to set the prototype here)
+ __proto__: sayMixin, // (or we could use Object.create to set the prototype here)
sayHi() {
*!*
@@ -101,9 +101,9 @@ Here's the diagram (see the right part):

-That's because methods `sayHi` and `sayBye` were initially created in `sayHiMixin`. So even though they got copied, their `[[HomeObject]]` internal property references `sayHiMixin`, as shown in the picture above.
+That's because methods `sayHi` and `sayBye` were initially created in `sayHiMixin`. So even though they got copied, their `[[HomeObject]]` internal property references `sayHiMixin`, as shown on the picture above.
-As `super` looks for parent methods in `[[HomeObject]].[[Prototype]]`, that means it searches `sayHiMixin.[[Prototype]]`.
+As `super` looks for parent methods in `[[HomeObject]].[[Prototype]]`, that means it searches `sayHiMixin.[[Prototype]]`, not `User.[[Prototype]]`.
## EventMixin
@@ -112,7 +112,7 @@ Now let's make a mixin for real life.
An important feature of many browser objects (for instance) is that they can generate events. Events are a great way to "broadcast information" to anyone who wants it. So let's make a mixin that allows us to easily add event-related functions to any class/object.
- The mixin will provide a method `.trigger(name, [...data])` to "generate an event" when something important happens to it. The `name` argument is a name of the event, optionally followed by additional arguments with event data.
-- Also the method `.on(name, handler)` that adds `handler` function as the listener to events with the given name. It will be called when an event with the given `name` triggers, and get the arguments from the `.trigger` call.
+- Also the method `.on(name, handler)` that adds `handler` function as the listener to events with the given name. It will be called when an event with the given `name` triggers, and get the arguments from `.trigger` call.
- ...And the method `.off(name, handler)` that removes the `handler` listener.
After adding the mixin, an object `user` will be able to generate an event `"login"` when the visitor logs in. And another object, say, `calendar` may want to listen for such events to load the calendar for the logged-in person.
@@ -140,7 +140,7 @@ let eventMixin = {
* menu.off('select', handler)
*/
off(eventName, handler) {
- let handlers = this._eventHandlers?.[eventName];
+ let handlers = this._eventHandlers && this._eventHandlers[eventName];
if (!handlers) return;
for (let i = 0; i < handlers.length; i++) {
if (handlers[i] === handler) {
@@ -154,7 +154,7 @@ let eventMixin = {
* this.trigger('select', data1, data2);
*/
trigger(eventName, ...args) {
- if (!this._eventHandlers?.[eventName]) {
+ if (!this._eventHandlers || !this._eventHandlers[eventName]) {
return; // no handlers for that event name
}
diff --git a/1-js/09-classes/07-mixins/head.html b/1-js/09-classes/07-mixins/head.html
index 20e3a635..77ea38b2 100644
--- a/1-js/09-classes/07-mixins/head.html
+++ b/1-js/09-classes/07-mixins/head.html
@@ -18,7 +18,7 @@ let eventMixin = {
* menu.off('select', handler)
*/
off(eventName, handler) {
- let handlers = this._eventHandlers?.[eventName];
+ let handlers = this._eventHandlers && this._eventHandlers[eventName];
if (!handlers) return;
for(let i = 0; i < handlers.length; i++) {
if (handlers[i] == handler) {
diff --git a/1-js/09-classes/07-mixins/mixin-inheritance.svg b/1-js/09-classes/07-mixins/mixin-inheritance.svg
index 1fdc2239..aaa8cb7d 100644
--- a/1-js/09-classes/07-mixins/mixin-inheritance.svg
+++ b/1-js/09-classes/07-mixins/mixin-inheritance.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/10-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
index ec0dabc9..303431d6 100644
--- a/1-js/10-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
@@ -1,8 +1,8 @@
The difference becomes obvious when we look at the code inside a function.
-The behavior is different if there's a "jump out" of `try...catch`.
+The behavior is different if there's a "jump out" of `try..catch`.
-For instance, when there's a `return` inside `try...catch`. The `finally` clause works in case of *any* exit from `try...catch`, even via the `return` statement: right after `try...catch` is done, but before the calling code gets the control.
+For instance, when there's a `return` inside `try..catch`. The `finally` clause works in case of *any* exit from `try..catch`, even via the `return` statement: right after `try..catch` is done, but before the calling code gets the control.
```js run
function f() {
@@ -11,7 +11,7 @@ function f() {
*!*
return "result";
*/!*
- } catch (err) {
+ } catch (e) {
/// ...
} finally {
alert('cleanup!');
@@ -28,11 +28,11 @@ function f() {
try {
alert('start');
throw new Error("an error");
- } catch (err) {
+ } catch (e) {
// ...
if("can't handle the error") {
*!*
- throw err;
+ throw e;
*/!*
}
diff --git a/1-js/10-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
index b6dc8132..c573cc23 100644
--- a/1-js/10-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
@@ -6,12 +6,12 @@ importance: 5
Compare the two code fragments.
-1. The first one uses `finally` to execute the code after `try...catch`:
+1. The first one uses `finally` to execute the code after `try..catch`:
```js
try {
work work
- } catch (err) {
+ } catch (e) {
handle errors
} finally {
*!*
@@ -19,12 +19,12 @@ Compare the two code fragments.
*/!*
}
```
-2. The second fragment puts the cleaning right after `try...catch`:
+2. The second fragment puts the cleaning right after `try..catch`:
```js
try {
work work
- } catch (err) {
+ } catch (e) {
handle errors
}
diff --git a/1-js/10-error-handling/1-try-catch/article.md b/1-js/10-error-handling/1-try-catch/article.md
index a928da28..0356fa2a 100644
--- a/1-js/10-error-handling/1-try-catch/article.md
+++ b/1-js/10-error-handling/1-try-catch/article.md
@@ -1,14 +1,14 @@
-# Error handling, "try...catch"
+# Error handling, "try..catch"
No matter how great we are at programming, sometimes our scripts have errors. They may occur because of our mistakes, an unexpected user input, an erroneous server response, and for a thousand other reasons.
Usually, a script "dies" (immediately stops) in case of an error, printing it to console.
-But there's a syntax construct `try...catch` that allows us to "catch" errors so the script can, instead of dying, do something more reasonable.
+But there's a syntax construct `try..catch` that allows to "catch" errors and, instead of dying, do something more reasonable.
-## The "try...catch" syntax
+## The "try..catch" syntax
-The `try...catch` construct has two main blocks: `try`, and then `catch`:
+The `try..catch` construct has two main blocks: `try`, and then `catch`:
```js
try {
@@ -25,14 +25,14 @@ try {
It works like this:
1. First, the code in `try {...}` is executed.
-2. If there were no errors, then `catch (err)` is ignored: the execution reaches the end of `try` and goes on, skipping `catch`.
-3. If an error occurs, then the `try` execution is stopped, and control flows to the beginning of `catch (err)`. The `err` variable (we can use any name for it) will contain an error object with details about what happened.
+2. If there were no errors, then `catch(err)` is ignored: the execution reaches the end of `try` and goes on, skipping `catch`.
+3. If an error occurs, then `try` execution is stopped, and the control flows to the beginning of `catch(err)`. The `err` variable (can use any name for it) will contain an error object with details about what happened.

-So, an error inside the `try {...}` block does not kill the script -- we have a chance to handle it in `catch`.
+So, an error inside the `try {…}` block does not kill the script: we have a chance to handle it in `catch`.
-Let's look at some examples.
+Let's see examples.
- An errorless example: shows `alert` `(1)` and `(2)`:
@@ -45,7 +45,7 @@ Let's look at some examples.
alert('End of try runs'); // *!*(2) <--*/!*
- } catch (err) {
+ } catch(err) {
alert('Catch is ignored, because there are no errors'); // (3)
@@ -64,7 +64,7 @@ Let's look at some examples.
alert('End of try (never reached)'); // (2)
- } catch (err) {
+ } catch(err) {
alert(`Error has occurred!`); // *!*(3) <--*/!*
@@ -72,45 +72,45 @@ Let's look at some examples.
```
-````warn header="`try...catch` only works for runtime errors"
-For `try...catch` to work, the code must be runnable. In other words, it should be valid JavaScript.
+````warn header="`try..catch` only works for runtime errors"
+For `try..catch` to work, the code must be runnable. In other words, it should be valid JavaScript.
It won't work if the code is syntactically wrong, for instance it has unmatched curly braces:
```js run
try {
{{{{{{{{{{{{
-} catch (err) {
+} catch(e) {
alert("The engine can't understand this code, it's invalid");
}
```
The JavaScript engine first reads the code, and then runs it. The errors that occur on the reading phase are called "parse-time" errors and are unrecoverable (from inside that code). That's because the engine can't understand the code.
-So, `try...catch` can only handle errors that occur in valid code. Such errors are called "runtime errors" or, sometimes, "exceptions".
+So, `try..catch` can only handle errors that occur in the valid code. Such errors are called "runtime errors" or, sometimes, "exceptions".
````
-````warn header="`try...catch` works synchronously"
-If an exception happens in "scheduled" code, like in `setTimeout`, then `try...catch` won't catch it:
+````warn header="`try..catch` works synchronously"
+If an exception happens in "scheduled" code, like in `setTimeout`, then `try..catch` won't catch it:
```js run
try {
setTimeout(function() {
noSuchVariable; // script will die here
}, 1000);
-} catch (err) {
+} catch (e) {
alert( "won't work" );
}
```
-That's because the function itself is executed later, when the engine has already left the `try...catch` construct.
+That's because the function itself is executed later, when the engine has already left the `try..catch` construct.
-To catch an exception inside a scheduled function, `try...catch` must be inside that function:
+To catch an exception inside a scheduled function, `try..catch` must be inside that function:
```js run
setTimeout(function() {
try {
- noSuchVariable; // try...catch handles the error!
+ noSuchVariable; // try..catch handles the error!
} catch {
alert( "error is caught here!" );
}
@@ -125,7 +125,7 @@ When an error occurs, JavaScript generates an object containing the details abou
```js
try {
// ...
-} catch (err) { // <-- the "error object", could use another word instead of err
+} catch(err) { // <-- the "error object", could use another word instead of err
// ...
}
```
@@ -150,7 +150,7 @@ try {
*!*
lalala; // error, variable is not defined!
*/!*
-} catch (err) {
+} catch(err) {
alert(err.name); // ReferenceError
alert(err.message); // lalala is not defined
alert(err.stack); // ReferenceError: lalala is not defined at (...call stack)
@@ -175,9 +175,9 @@ try {
}
```
-## Using "try...catch"
+## Using "try..catch"
-Let's explore a real-life use case of `try...catch`.
+Let's explore a real-life use case of `try..catch`.
As we already know, JavaScript supports the [JSON.parse(str)](mdn:js/JSON/parse) method to read JSON-encoded values.
@@ -201,11 +201,11 @@ You can find more detailed information about JSON in the chapter.
**If `json` is malformed, `JSON.parse` generates an error, so the script "dies".**
-Should we be satisfied with that? Of course not!
+Should we be satisfied with that? Of course, not!
This way, if something's wrong with the data, the visitor will never know that (unless they open the developer console). And people really don't like when something "just dies" without any error message.
-Let's use `try...catch` to handle the error:
+Let's use `try..catch` to handle the error:
```js run
let json = "{ bad json }";
@@ -217,12 +217,12 @@ try {
*/!*
alert( user.name ); // doesn't work
-} catch (err) {
+} catch (e) {
*!*
// ...the execution jumps here
alert( "Our apologies, the data has errors, we'll try to request it one more time." );
- alert( err.name );
- alert( err.message );
+ alert( e.name );
+ alert( e.message );
*/!*
}
```
@@ -245,7 +245,7 @@ try {
alert( user.name ); // no name!
*/!*
-} catch (err) {
+} catch (e) {
alert( "doesn't execute" );
}
```
@@ -294,11 +294,11 @@ Let's see what kind of error `JSON.parse` generates:
```js run
try {
JSON.parse("{ bad json o_O }");
-} catch (err) {
+} catch(e) {
*!*
- alert(err.name); // SyntaxError
+ alert(e.name); // SyntaxError
*/!*
- alert(err.message); // Unexpected token b in JSON at position 2
+ alert(e.message); // Unexpected token o in JSON at position 2
}
```
@@ -323,8 +323,8 @@ try {
alert( user.name );
-} catch (err) {
- alert( "JSON Error: " + err.message ); // JSON Error: Incomplete data: no name
+} catch(e) {
+ alert( "JSON Error: " + e.message ); // JSON Error: Incomplete data: no name
}
```
@@ -334,9 +334,9 @@ Now `catch` became a single place for all error handling: both for `JSON.parse`
## Rethrowing
-In the example above we use `try...catch` to handle incorrect data. But is it possible that *another unexpected error* occurs within the `try {...}` block? Like a programming error (variable is not defined) or something else, not just this "incorrect data" thing.
+In the example above we use `try..catch` to handle incorrect data. But is it possible that *another unexpected error* occurs within the `try {...}` block? Like a programming error (variable is not defined) or something else, not just that "incorrect data" thing.
-For example:
+Like this:
```js run
let json = '{ "age": 30 }'; // incomplete data
@@ -345,7 +345,7 @@ try {
user = JSON.parse(json); // <-- forgot to put "let" before user
// ...
-} catch (err) {
+} catch(err) {
alert("JSON Error: " + err); // JSON Error: ReferenceError: user is not defined
// (no JSON Error actually)
}
@@ -353,33 +353,29 @@ try {
Of course, everything's possible! Programmers do make mistakes. Even in open-source utilities used by millions for decades -- suddenly a bug may be discovered that leads to terrible hacks.
-In our case, `try...catch` is placed to catch "incorrect data" errors. But by its nature, `catch` gets *all* errors from `try`. Here it gets an unexpected error, but still shows the same `"JSON Error"` message. That's wrong and also makes the code more difficult to debug.
+In our case, `try..catch` is meant to catch "incorrect data" errors. But by its nature, `catch` gets *all* errors from `try`. Here it gets an unexpected error, but still shows the same `"JSON Error"` message. That's wrong and also makes the code more difficult to debug.
-To avoid such problems, we can employ the "rethrowing" technique. The rule is simple:
+Fortunately, we can find out which error we get, for instance from its `name`:
+
+```js run
+try {
+ user = { /*...*/ };
+} catch(e) {
+*!*
+ alert(e.name); // "ReferenceError" for accessing an undefined variable
+*/!*
+}
+```
+
+The rule is simple:
**Catch should only process errors that it knows and "rethrow" all others.**
The "rethrowing" technique can be explained in more detail as:
1. Catch gets all errors.
-2. In the `catch (err) {...}` block we analyze the error object `err`.
-3. If we don't know how to handle it, we do `throw err`.
-
-Usually, we can check the error type using the `instanceof` operator:
-
-```js run
-try {
- user = { /*...*/ };
-} catch (err) {
-*!*
- if (err instanceof ReferenceError) {
-*/!*
- alert('ReferenceError'); // "ReferenceError" for accessing an undefined variable
- }
-}
-```
-
-We can also get the error class name from `err.name` property. All native errors have it. Another option is to read `err.constructor.name`.
+2. In `catch(err) {...}` block we analyze the error object `err`.
+2. If we don't know how to handle it, then we do `throw err`.
In the code below, we use rethrowing so that `catch` only handles `SyntaxError`:
@@ -399,24 +395,24 @@ try {
alert( user.name );
-} catch (err) {
+} catch(e) {
*!*
- if (err instanceof SyntaxError) {
- alert( "JSON Error: " + err.message );
+ if (e.name == "SyntaxError") {
+ alert( "JSON Error: " + e.message );
} else {
- throw err; // rethrow (*)
+ throw e; // rethrow (*)
}
*/!*
}
```
-The error throwing on line `(*)` from inside `catch` block "falls out" of `try...catch` and can be either caught by an outer `try...catch` construct (if it exists), or it kills the script.
+The error throwing on line `(*)` from inside `catch` block "falls out" of `try..catch` and can be either caught by an outer `try..catch` construct (if it exists), or it kills the script.
So the `catch` block actually handles only errors that it knows how to deal with and "skips" all others.
-The example below demonstrates how such errors can be caught by one more level of `try...catch`:
+The example below demonstrates how such errors can be caught by one more level of `try..catch`:
```js run
function readData() {
@@ -427,11 +423,11 @@ function readData() {
*!*
blabla(); // error!
*/!*
- } catch (err) {
+ } catch (e) {
// ...
- if (!(err instanceof SyntaxError)) {
+ if (e.name != 'SyntaxError') {
*!*
- throw err; // rethrow (don't know how to deal with it)
+ throw e; // rethrow (don't know how to deal with it)
*/!*
}
}
@@ -439,20 +435,20 @@ function readData() {
try {
readData();
-} catch (err) {
+} catch (e) {
*!*
- alert( "External catch got: " + err ); // caught it!
+ alert( "External catch got: " + e ); // caught it!
*/!*
}
```
-Here `readData` only knows how to handle `SyntaxError`, while the outer `try...catch` knows how to handle everything.
+Here `readData` only knows how to handle `SyntaxError`, while the outer `try..catch` knows how to handle everything.
-## try...catch...finally
+## try..catch..finally
Wait, that's not all.
-The `try...catch` construct may have one more code clause: `finally`.
+The `try..catch` construct may have one more code clause: `finally`.
If it exists, it runs in all cases:
@@ -464,7 +460,7 @@ The extended syntax looks like this:
```js
*!*try*/!* {
... try to execute the code ...
-} *!*catch*/!* (err) {
+} *!*catch*/!*(e) {
... handle errors ...
} *!*finally*/!* {
... execute always ...
@@ -477,7 +473,7 @@ Try running this code:
try {
alert( 'try' );
if (confirm('Make an error?')) BAD_CODE();
-} catch (err) {
+} catch (e) {
alert( 'catch' );
} finally {
alert( 'finally' );
@@ -513,7 +509,7 @@ let start = Date.now();
try {
result = fib(num);
-} catch (err) {
+} catch (e) {
result = 0;
*!*
} finally {
@@ -531,14 +527,14 @@ You can check by running the code with entering `35` into `prompt` -- it execute
In other words, the function may finish with `return` or `throw`, that doesn't matter. The `finally` clause executes in both cases.
-```smart header="Variables are local inside `try...catch...finally`"
-Please note that `result` and `diff` variables in the code above are declared *before* `try...catch`.
+```smart header="Variables are local inside `try..catch..finally`"
+Please note that `result` and `diff` variables in the code above are declared *before* `try..catch`.
Otherwise, if we declared `let` in `try` block, it would only be visible inside of it.
```
````smart header="`finally` and `return`"
-The `finally` clause works for *any* exit from `try...catch`. That includes an explicit `return`.
+The `finally` clause works for *any* exit from `try..catch`. That includes an explicit `return`.
In the example below, there's a `return` in `try`. In this case, `finally` is executed just before the control returns to the outer code.
@@ -550,7 +546,7 @@ function func() {
return 1;
*/!*
- } catch (err) {
+ } catch (e) {
/* ... */
} finally {
*!*
@@ -563,9 +559,9 @@ alert( func() ); // first works alert from finally, and then this one
```
````
-````smart header="`try...finally`"
+````smart header="`try..finally`"
-The `try...finally` construct, without `catch` clause, is also useful. We apply it when we don't want to handle errors here (let them fall through), but want to be sure that processes that we started are finalized.
+The `try..finally` construct, without `catch` clause, is also useful. We apply it when we don't want to handle errors here (let them fall through), but want to be sure that processes that we started are finalized.
```js
function func() {
@@ -586,11 +582,11 @@ In the code above, an error inside `try` always falls out, because there's no `c
The information from this section is not a part of the core JavaScript.
```
-Let's imagine we've got a fatal error outside of `try...catch`, and the script died. Like a programming error or some other terrible thing.
+Let's imagine we've got a fatal error outside of `try..catch`, and the script died. Like a programming error or something else terrible.
-Is there a way to react on such occurrences? We may want to log the error, show something to the user (normally they don't see error messages), etc.
+Is there a way to react on such occurrences? We may want to log the error, show something to the user (normally they don't see error messages) etc.
-There is none in the specification, but environments usually provide it, because it's really useful. For instance, Node.js has [`process.on("uncaughtException")`](https://nodejs.org/api/process.html#process_event_uncaughtexception) for that. And in the browser we can assign a function to the special [window.onerror](mdn:api/GlobalEventHandlers/onerror) property, that will run in case of an uncaught error.
+There is none in the specification, but environments usually provide it, because it's really useful. For instance, Node.js has [`process.on("uncaughtException")`](https://nodejs.org/api/process.html#process_event_uncaughtexception) for that. And in the browser we can assign a function to special [window.onerror](mdn:api/GlobalEventHandlers/onerror) property, that will run in case of an uncaught error.
The syntax:
@@ -643,14 +639,14 @@ They work like this:
## Summary
-The `try...catch` construct allows to handle runtime errors. It literally allows to "try" running the code and "catch" errors that may occur in it.
+The `try..catch` construct allows to handle runtime errors. It literally allows to "try" running the code and "catch" errors that may occur in it.
The syntax is:
```js
try {
// run this code
-} catch (err) {
+} catch(err) {
// if an error happened, then jump here
// err is the error object
} finally {
@@ -658,7 +654,7 @@ try {
}
```
-There may be no `catch` section or no `finally`, so shorter constructs `try...catch` and `try...finally` are also valid.
+There may be no `catch` section or no `finally`, so shorter constructs `try..catch` and `try..finally` are also valid.
Error objects have following properties:
@@ -666,10 +662,10 @@ Error objects have following properties:
- `name` -- the string with error name (error constructor name).
- `stack` (non-standard, but well-supported) -- the stack at the moment of error creation.
-If an error object is not needed, we can omit it by using `catch {` instead of `catch (err) {`.
+If an error object 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 very important 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.
-Even if we don't have `try...catch`, most environments allow us to setup a "global" error handler to catch errors that "fall out". In-browser, that's `window.onerror`.
+Even if we don't have `try..catch`, most environments allow us to setup a "global" error handler to catch errors that "fall out". In-browser, that's `window.onerror`.
diff --git a/1-js/10-error-handling/1-try-catch/try-catch-flow.svg b/1-js/10-error-handling/1-try-catch/try-catch-flow.svg
index 2c0d7134..ab17dcda 100644
--- a/1-js/10-error-handling/1-try-catch/try-catch-flow.svg
+++ b/1-js/10-error-handling/1-try-catch/try-catch-flow.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/10-error-handling/2-custom-errors/1-format-error/solution.md b/1-js/10-error-handling/2-custom-errors/1-format-error/solution.md
index 754e68f9..bb6b74cf 100644
--- a/1-js/10-error-handling/2-custom-errors/1-format-error/solution.md
+++ b/1-js/10-error-handling/2-custom-errors/1-format-error/solution.md
@@ -2,7 +2,7 @@
class FormatError extends SyntaxError {
constructor(message) {
super(message);
- this.name = this.constructor.name;
+ this.name = "FormatError";
}
}
diff --git a/1-js/10-error-handling/2-custom-errors/article.md b/1-js/10-error-handling/2-custom-errors/article.md
index 91828931..e5f23a42 100644
--- a/1-js/10-error-handling/2-custom-errors/article.md
+++ b/1-js/10-error-handling/2-custom-errors/article.md
@@ -2,11 +2,11 @@
When we develop something, we often need our own error classes to reflect specific things that may go wrong in our tasks. For errors in network operations we may need `HttpError`, for database operations `DbError`, for searching operations `NotFoundError` and so on.
-Our errors should support basic error properties like `message`, `name` and, preferably, `stack`. But they also may have other properties of their own, e.g. `HttpError` objects may have a `statusCode` property with a value like `404` or `403` or `500`.
+Our errors should support basic error properties like `message`, `name` and, preferably, `stack`. But they also may have other properties of their own, e.g. `HttpError` objects may have `statusCode` property with a value like `404` or `403` or `500`.
JavaScript allows to use `throw` with any argument, so technically our custom error classes don't need to inherit from `Error`. But if we inherit, then it becomes possible to use `obj instanceof Error` to identify error objects. So it's better to inherit from it.
-As the application grows, our own errors naturally form a hierarchy. For instance, `HttpTimeoutError` may inherit from `HttpError`, and so on.
+As the application grows, our own errors naturally form a hierarchy, for instance `HttpTimeoutError` may inherit from `HttpError`, and so on.
## Extending Error
@@ -21,9 +21,9 @@ Internally, we'll use `JSON.parse`. If it receives malformed `json`, then it thr
Our function `readUser(json)` will not only read JSON, but check ("validate") the data. If there are no required fields, or the format is wrong, then that's an error. And that's not a `SyntaxError`, because the data is syntactically correct, but another kind of error. We'll call it `ValidationError` and create a class for it. An error of that kind should also carry the information about the offending field.
-Our `ValidationError` class should inherit from the `Error` class.
+Our `ValidationError` class should inherit from the built-in `Error` class.
-The `Error` class is built-in, but here's its approximate code so we can understand what we're extending:
+That class is built-in, but here's its approximate code so we can understand what we're extending:
```js
// The "pseudocode" for the built-in Error class defined by JavaScript itself
@@ -117,15 +117,15 @@ We could also look at `err.name`, like this:
// instead of (err instanceof SyntaxError)
} else if (err.name == "SyntaxError") { // (*)
// ...
-```
+```
The `instanceof` version is much better, because in the future we are going to extend `ValidationError`, make subtypes of it, like `PropertyRequiredError`. And `instanceof` check will continue to work for new inheriting classes. So that's future-proof.
-Also it's important that if `catch` meets an unknown error, then it rethrows it in the line `(**)`. The `catch` block only knows how to handle validation and syntax errors, other kinds (caused by a typo in the code or other unknown reasons) should fall through.
+Also it's important that if `catch` meets an unknown error, then it rethrows it in the line `(**)`. The `catch` block only knows how to handle validation and syntax errors, other kinds (due to a typo in the code or other unknown ones) should fall through.
## Further inheritance
-The `ValidationError` class is very generic. Many things may go wrong. The property may be absent or it may be in a wrong format (like a string value for `age` instead of a number). Let's make a more concrete class `PropertyRequiredError`, exactly for absent properties. It will carry additional information about the property that's missing.
+The `ValidationError` class is very generic. Many things may go wrong. The property may be absent or it may be in a wrong format (like a string value for `age`). Let's make a more concrete class `PropertyRequiredError`, exactly for absent properties. It will carry additional information about the property that's missing.
```js run
class ValidationError extends Error {
@@ -180,7 +180,7 @@ try {
The new class `PropertyRequiredError` is easy to use: we only need to pass the property name: `new PropertyRequiredError(property)`. The human-readable `message` is generated by the constructor.
-Please note that `this.name` in `PropertyRequiredError` constructor is again assigned manually. That may become a bit tedious -- to assign `this.name = ` in every custom error class. We can avoid it by making our own "basic error" class that assigns `this.name = this.constructor.name`. And then inherit all our custom errors from it.
+Please note that `this.name` in `PropertyRequiredError` constructor is again assigned manually. That may become a bit tedious -- to assign `this.name = ` in every custom error class. We can avoid it by making our own "basic error" class that assigns `this.name = this.constructor.name`. And then inherit all ours custom errors from it.
Let's call it `MyError`.
@@ -215,39 +215,11 @@ Now custom errors are much shorter, especially `ValidationError`, as we got rid
The purpose of the function `readUser` in the code above is "to read the user data". There may occur different kinds of errors in the process. Right now we have `SyntaxError` and `ValidationError`, but in the future `readUser` function may grow and probably generate other kinds of errors.
-The code which calls `readUser` should handle these errors. Right now it uses multiple `if`s in the `catch` block, that check the class and handle known errors and rethrow the unknown ones.
+The code which calls `readUser` should handle these errors. Right now it uses multiple `if`s in the `catch` block, that check the class and handle known errors and rethrow the unknown ones. But if the `readUser` function generates several kinds of errors, then we should ask ourselves: do we really want to check for all error types one-by-one in every code that calls `readUser`?
-The scheme is like this:
+Often the answer is "No": the outer code wants to be "one level above all that", it just wants to have some kind of "data reading error" -- why exactly it happened is often irrelevant (the error message describes it). Or, even better, it could have a way to get the error details, but only if we need to.
-```js
-try {
- ...
- readUser() // the potential error source
- ...
-} catch (err) {
- if (err instanceof ValidationError) {
- // handle validation errors
- } else if (err instanceof SyntaxError) {
- // handle syntax errors
- } else {
- throw err; // unknown error, rethrow it
- }
-}
-```
-
-In the code above we can see two types of errors, but there can be more.
-
-If the `readUser` function generates several kinds of errors, then we should ask ourselves: do we really want to check for all error types one-by-one every time?
-
-Often the answer is "No": we'd like to be "one level above all that". We just want to know if there was a "data reading error" -- why exactly it happened is often irrelevant (the error message describes it). Or, even better, we'd like to have a way to get the error details, but only if we need to.
-
-The technique that we describe here is called "wrapping exceptions".
-
-1. We'll make a new class `ReadError` to represent a generic "data reading" error.
-2. The function `readUser` will catch data reading errors that occur inside it, such as `ValidationError` and `SyntaxError`, and generate a `ReadError` instead.
-3. The `ReadError` object will keep the reference to the original error in its `cause` property.
-
-Then the code that calls `readUser` will only have to check for `ReadError`, not for every kind of data reading errors. And if it needs more details of an error, it can check its `cause` property.
+So let's make a new class `ReadError` to represent such errors. If an error occurs inside `readUser`, we'll catch it there and generate `ReadError`. We'll also keep the reference to the original error in its `cause` property. Then the outer code will only have to check for `ReadError`.
Here's the code that defines `ReadError` and demonstrates its use in `readUser` and `try..catch`:
@@ -319,12 +291,12 @@ try {
In the code above, `readUser` works exactly as described -- catches syntax and validation errors and throws `ReadError` errors instead (unknown errors are rethrown as usual).
-So the outer code checks `instanceof ReadError` and that's it. No need to list all possible error types.
+So the outer code checks `instanceof ReadError` and that's it. No need to list possible all error types.
-The approach is called "wrapping exceptions", because we take "low level" exceptions and "wrap" them into `ReadError` that is more abstract. It is widely used in object-oriented programming.
+The approach is called "wrapping exceptions", because we take "low level exceptions" and "wrap" them into `ReadError` that is more abstract and more convenient to use for the calling code. It is widely used in object-oriented programming.
## Summary
-- We can inherit from `Error` and other built-in error classes normally. We just need to take care of the `name` property and don't forget to call `super`.
-- We can use `instanceof` to check for particular errors. It also works with inheritance. But sometimes we have an error object coming from a 3rd-party library and there's no easy way to get its class. Then `name` property can be used for such checks.
+- We can inherit from `Error` and other built-in error classes normally, just need to take care of `name` property and don't forget to call `super`.
+- We can use `instanceof` to check for particular errors. It also works with inheritance. But sometimes we have an error object coming from the 3rd-party library and there's no easy way to get the class. Then `name` property can be used for such checks.
- Wrapping exceptions is a widespread technique: a function handles low-level exceptions and creates higher-level errors instead of various low-level ones. Low-level exceptions sometimes become properties of that object like `err.cause` in the examples above, but that's not strictly required.
diff --git a/1-js/05-data-types/05-array-methods/12-reduce-object/solution.md b/1-js/11-async/01-callbacks/01-animate-circle-callback/solution.md
similarity index 100%
rename from 1-js/05-data-types/05-array-methods/12-reduce-object/solution.md
rename to 1-js/11-async/01-callbacks/01-animate-circle-callback/solution.md
diff --git a/7-animation/2-css-animations/4-animate-circle-callback/solution.view/index.html b/1-js/11-async/01-callbacks/01-animate-circle-callback/solution.view/index.html
similarity index 94%
rename from 7-animation/2-css-animations/4-animate-circle-callback/solution.view/index.html
rename to 1-js/11-async/01-callbacks/01-animate-circle-callback/solution.view/index.html
index 64746e85..b2192681 100644
--- a/7-animation/2-css-animations/4-animate-circle-callback/solution.view/index.html
+++ b/1-js/11-async/01-callbacks/01-animate-circle-callback/solution.view/index.html
@@ -10,7 +10,7 @@
text-align: center;
}
.circle {
- transition-property: width, height;
+ transition-property: width, height, margin-left, margin-top;
transition-duration: 2s;
position: fixed;
transform: translateX(-50%) translateY(-50%);
diff --git a/7-animation/2-css-animations/4-animate-circle-callback/task.md b/1-js/11-async/01-callbacks/01-animate-circle-callback/task.md
similarity index 100%
rename from 7-animation/2-css-animations/4-animate-circle-callback/task.md
rename to 1-js/11-async/01-callbacks/01-animate-circle-callback/task.md
diff --git a/1-js/11-async/01-callbacks/article.md b/1-js/11-async/01-callbacks/article.md
index 57115a90..c2f67c6c 100644
--- a/1-js/11-async/01-callbacks/article.md
+++ b/1-js/11-async/01-callbacks/article.md
@@ -2,44 +2,30 @@
# Introduction: callbacks
-```warn header="We use browser methods in examples here"
-To demonstrate the use of callbacks, promises and other abstract concepts, we'll be using some browser methods: specifically, loading scripts and performing simple document manipulations.
+Many actions in JavaScript are *asynchronous*.
-If you're not familiar with these methods, and their usage in the examples is confusing, you may want to read a few chapters from the [next part](/document) of the tutorial.
-
-Although, we'll try to make things clear anyway. There won't be anything really complex browser-wise.
-```
-
-Many functions are provided by JavaScript host environments that allow you to schedule *asynchronous* actions. In other words, actions that we initiate now, but they finish later.
-
-For instance, one such function is the `setTimeout` function.
-
-There are other real-world examples of asynchronous actions, e.g. loading scripts and modules (we'll cover them in later chapters).
-
-Take a look at the function `loadScript(src)`, that loads a script with the given `src`:
+For instance, take a look at the function `loadScript(src)`:
```js
function loadScript(src) {
- // creates a
```
-```smart
-In the browser, we can make a variable window-level global by explicitly assigning it to a `window` property, e.g. `window.user = "John"`.
-
-Then all scripts will see it, both with `type="module"` and without it.
-
-That said, making such global variables is frowned upon. Please try to avoid them.
-```
+If we really need to make a window-level global 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
-If the same module is imported into multiple other modules, its code is executed only once, upon the first import. Then its exports are given to all further importers.
+If the same module is imported into multiple other places, its code is executed only the first time, then exports are given to all importers.
-The one-time evaluation has important consequences, that we should be aware of.
-
-Let's see a couple of examples.
+That has important consequences. Let's see that on examples.
First, if executing a module code brings side-effects, like showing a message, then importing it multiple times will trigger it only once -- the first time:
@@ -146,11 +129,9 @@ import `./alert.js`; // Module is evaluated!
import `./alert.js`; // (shows nothing)
```
-The second import shows nothing, because the module has already been evaluated.
+In practice, top-level module code is mostly used for initialization, creation of internal data structures, and if we want something to be reusable -- export it.
-There's a rule: top-level module code should be used for initialization, creation of module-specific internal data structures. If we need to make something callable multiple times - we should export it as a function, like we did with `sayHi` above.
-
-Now, let's consider a deeper example.
+Now, a more advanced example.
Let's say, a module exports an object:
@@ -175,67 +156,54 @@ import {admin} from './admin.js';
alert(admin.name); // Pete
*!*
-// Both 1.js and 2.js reference the same admin object
+// Both 1.js and 2.js imported the same object
// Changes made in 1.js are visible in 2.js
*/!*
```
-As you can see, when `1.js` changes the `name` property in the imported `admin`, then `2.js` can see the new `admin.name`.
+So, let's reiterate -- the module is executed only once. Exports are generated, and then they are shared between importers, so if something changes the `admin` object, other modules will see that.
-That's exactly because the module is executed only once. Exports are generated, and then they are shared between importers, so if something changes the `admin` object, other importers will see that.
+Such behavior allows to *configure* modules on first import. We can setup its properties once, and then in further imports it's ready.
-**Such behavior is actually very convenient, because it allows us to *configure* modules.**
-
-In other words, a module can provide a generic functionality that needs a setup. E.g. authentication needs credentials. Then it can export a configuration object expecting the outer code to assign to it.
-
-Here's the classical pattern:
-1. A module exports some means of configuration, e.g. a configuration object.
-2. On the first import we initialize it, write to its properties. The top-level application script may do that.
-3. Further imports use the module.
-
-For instance, the `admin.js` module may provide certain functionality (e.g. authentication), but expect the credentials to come into the `config` object from outside:
+For instance, `admin.js` module may provide certain functionality, but expect the credentials to come into the `admin` object from outside:
```js
// 📁 admin.js
-export let config = { };
+export let admin = { };
export function sayHi() {
- alert(`Ready to serve, ${config.user}!`);
+ alert(`Ready to serve, ${admin.name}!`);
}
```
-Here, `admin.js` exports the `config` object (initially empty, but may have default properties too).
-
-Then in `init.js`, the first script of our app, we import `config` from it and set `config.user`:
+In `init.js`, the first script of our app, we set `admin.name`. Then everyone will see it, including calls made from inside `admin.js` itself:
```js
// 📁 init.js
-import {config} from './admin.js';
-config.user = "Pete";
+import {admin} from './admin.js';
+admin.name = "Pete";
```
-...Now the module `admin.js` is configured.
-
-Further importers can call it, and it correctly shows the current user:
+Another module can also see `admin.name`:
```js
-// 📁 another.js
-import {sayHi} from './admin.js';
+// 📁 other.js
+import {admin, sayHi} from './admin.js';
+
+alert(admin.name); // *!*Pete*/!*
sayHi(); // Ready to serve, *!*Pete*/!*!
```
-
### import.meta
The object `import.meta` contains the information about the current module.
-Its content depends on the environment. In the browser, it contains the URL of the script, or a current webpage URL 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:
```html run height=0
```
@@ -261,18 +229,18 @@ Compare it to non-module scripts, where `this` is a global object:
There are also several browser-specific differences of scripts with `type="module"` compared to regular ones.
-You may want to skip this section for now if you're reading for the first time, or if you don't use JavaScript in a browser.
+You may want skip this section 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:script-async-defer)), for both external and inline scripts.
In other words:
-- downloading external module scripts `
@@ -296,21 +264,21 @@ Compare to regular script below:
```
-Please note: the second script actually runs before the first! So we'll see `undefined` first, and then `object`.
+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 we wait for the document to be processed. The regular script runs immediately, so we see its output first.
+That's because modules are deferred, so we 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 the HTML page shows up as it loads, and JavaScript modules run after that, so the user may see the page before the JavaScript application is ready. Some functionality may not work yet. We should put "loading indicators", or otherwise ensure that the visitor won't be confused by that.
+When using modules, we should be aware that HTML-page shows up as it loads, and JavaScript modules run after that, so the user may see the page before the JavaScript application is ready. Some functionality may not work yet. We should put "loading indicators", or otherwise ensure that the visitor won't be confused by that.
### Async works on inline scripts
-For non-module scripts, the `async` attribute only works on external scripts. Async scripts run immediately when ready, independently of other scripts or the HTML document.
+For non-module scripts, `async` attribute only works on external scripts. Async scripts run immediately when ready, independently of other scripts or the HTML document.
-For module scripts, it works on inline scripts as well.
+For module scripts, it works on any scripts.
-For example, the inline script below has `async`, so it doesn't wait for anything.
+For example, the script below has `async`, so it doesn't wait for anyone.
-It performs the import (fetches `./analytics.js`) and runs when ready, even if the HTML document is not finished yet, or if other scripts are still pending.
+It performs the import (fetches `./analytics.js`) and runs when ready, even if HTML document is not finished yet, or if other scripts are still pending.
That's good for functionality that doesn't depend on anything, like counters, ads, document-level event listeners.
@@ -328,7 +296,7 @@ That's good for functionality that doesn't depend on anything, like counters, ad
External scripts that have `type="module"` are different in two aspects:
-1. External scripts with the same `src` run only once:
+1. External scripts with same `src` run only once:
```html
@@ -354,11 +322,11 @@ import {sayHi} from 'sayHi'; // Error, "bare" module
// the module must have a path, e.g. './sayHi.js' or wherever the module is
```
-Certain environments, like Node.js or bundle tools allow bare modules, without any path, as they have their own ways for finding modules and hooks to fine-tune them. But browsers do not support bare modules yet.
+Certain environments, like Node.js or bundle tools allow bare modules, without any path, 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 an unknown type are just ignored. For them, it's possible to provide a fallback using the `nomodule` attribute:
+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
@@ -143,7 +137,7 @@ drawHtmlTree(node4, 'div.domtree', 690, 360);
````warn header="Tables always have ``"
-An interesting "special case" is tables. By DOM specification they must have `` tag, but HTML text may omit it. Then the browser creates `` in the DOM automatically.
+An interesting "special case" is tables. By the DOM specification they must have ``, but HTML text may (officially) omit it. Then the browser creates `` in the DOM automatically.
For the HTML:
@@ -160,7 +154,7 @@ let node5 = {"name":"TABLE","nodeType":1,"children":[{"name":"TBODY","nodeType":
drawHtmlTree(node5, 'div.domtree', 600, 200);
-You see? The `` appeared out of nowhere. We should keep this in mind while working with tables to avoid surprises.
+You see? The `` appeared out of nowhere. You should keep this in mind while working with tables to avoid surprises.
````
## Other node types
@@ -188,7 +182,7 @@ For example, comments:
@@ -199,7 +193,7 @@ We may think -- why is a comment added to the DOM? It doesn't affect the visual
**Everything in HTML, even comments, becomes a part of the DOM.**
-Even the `` directive at the very beginning of HTML is also a DOM node. It's in the DOM tree right before ``. Few people know about that. We are not going to touch that node, we even don't draw it on diagrams, but it's there.
+Even the `` directive at the very beginning of HTML is also a DOM node. It's in the DOM tree right before ``. We are not going to touch that node, we even don't draw it on diagrams for that reason, but it's there.
The `document` object that represents the whole document is, formally, a DOM node as well.
diff --git a/2-ui/1-document/02-dom-nodes/domconsole0.svg b/2-ui/1-document/02-dom-nodes/domconsole0.svg
index eb99f193..c0096060 100644
--- a/2-ui/1-document/02-dom-nodes/domconsole0.svg
+++ b/2-ui/1-document/02-dom-nodes/domconsole0.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/2-ui/1-document/02-dom-nodes/domconsole1.svg b/2-ui/1-document/02-dom-nodes/domconsole1.svg
index 02ef5f0a..db92359d 100644
--- a/2-ui/1-document/02-dom-nodes/domconsole1.svg
+++ b/2-ui/1-document/02-dom-nodes/domconsole1.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/2-ui/1-document/02-dom-nodes/elk.svg b/2-ui/1-document/02-dom-nodes/elk.svg
index 448eea9d..19ea221d 100644
--- a/2-ui/1-document/02-dom-nodes/elk.svg
+++ b/2-ui/1-document/02-dom-nodes/elk.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/2-ui/1-document/02-dom-nodes/inspect.svg b/2-ui/1-document/02-dom-nodes/inspect.svg
index 60696ec0..658ee5ea 100644
--- a/2-ui/1-document/02-dom-nodes/inspect.svg
+++ b/2-ui/1-document/02-dom-nodes/inspect.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/2-ui/1-document/03-dom-navigation/article.md b/2-ui/1-document/03-dom-navigation/article.md
index b5f03098..3ff53c9a 100644
--- a/2-ui/1-document/03-dom-navigation/article.md
+++ b/2-ui/1-document/03-dom-navigation/article.md
@@ -149,7 +149,7 @@ There are two important consequences:
The first thing is nice. The second is tolerable, because we can use `Array.from` to create a "real" array from the collection, if we want array methods:
```js run
- alert( Array.from(document.body.childNodes).filter ); // function
+ alert( Array.from(document.body.childNodes).filter ); // now it's there
```
```warn header="DOM collections are read-only"
@@ -157,7 +157,7 @@ DOM collections, and even more -- *all* navigation properties listed in this cha
We can't replace a child by something else by assigning `childNodes[i] = ...`.
-Changing DOM needs other methods. We will see them in the next chapter.
+Changing the DOM requires other methods. We will see them in the next chapter.
```
```warn header="DOM collections are live"
@@ -201,7 +201,7 @@ The parent is available as `parentNode`.
For example:
-```js run
+```js
// parent of is
alert( document.body.parentNode === document.documentElement ); // true
@@ -214,7 +214,7 @@ alert( document.body.previousSibling ); // HTMLHeadElement
## Element-only navigation
-Navigation properties listed above refer to *all* nodes. For instance, in `childNodes` we can see both text nodes, element nodes, and even comment nodes if they exist.
+Navigation properties listed above refer to *all* nodes. For instance, in `childNodes` we can see both text nodes, element nodes, and even comment nodes if there exist.
But for many tasks we don't want text or comment nodes. We want to manipulate element nodes that represent tags and form the structure of the page.
@@ -226,7 +226,7 @@ The links are similar to those given above, just with `Element` word inside:
- `children` -- only those children that are element nodes.
- `firstElementChild`, `lastElementChild` -- first and last element children.
-- `previousElementSibling`, `nextElementSibling` -- neighbor elements.
+- `previousElementSibling`, `nextElementSibling` -- neighbour elements.
- `parentElement` -- parent element.
````smart header="Why `parentElement`? Can the parent be *not* an element?"
@@ -280,12 +280,12 @@ Till now we described the basic navigation properties.
Certain types of DOM elements may provide additional properties, specific to their type, for convenience.
-Tables are a great example of that, and represent a particularly important case:
+Tables are a great example and a particularly important case for that.
**The `
`** element supports (in addition to the given above) these properties:
- `table.rows` -- the collection of `
` elements of the table.
- `table.caption/tHead/tFoot` -- references to elements `
`, `
`, `
`.
-- `table.tBodies` -- the collection of `` elements (can be many according to the standard, but there will always be at least one -- even if it is not in the source HTML, the browser will put it in the DOM).
+- `table.tBodies` -- the collection of `` elements (can be many according to the standard).
**``, ``, ``** elements provide the `rows` property:
- `tbody.rows` -- the collection of `
` inside.
@@ -311,9 +311,8 @@ An example of usage:
```
@@ -323,7 +322,7 @@ There are also additional navigation properties for HTML forms. We'll look at th
## Summary
-Given a DOM node, we can go to its immediate neighbors using navigation properties.
+Given a DOM node, we can go to its immediate neighbours using navigation properties.
There are two main sets of them:
diff --git a/2-ui/1-document/03-dom-navigation/dom-links-elements.svg b/2-ui/1-document/03-dom-navigation/dom-links-elements.svg
index fd0b2826..19028553 100644
--- a/2-ui/1-document/03-dom-navigation/dom-links-elements.svg
+++ b/2-ui/1-document/03-dom-navigation/dom-links-elements.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/2-ui/1-document/03-dom-navigation/dom-links.svg b/2-ui/1-document/03-dom-navigation/dom-links.svg
index 6c34bca4..df252deb 100644
--- a/2-ui/1-document/03-dom-navigation/dom-links.svg
+++ b/2-ui/1-document/03-dom-navigation/dom-links.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/2-ui/1-document/04-searching-elements-dom/article.md b/2-ui/1-document/04-searching-elements-dom/article.md
index de47eac9..2f541688 100644
--- a/2-ui/1-document/04-searching-elements-dom/article.md
+++ b/2-ui/1-document/04-searching-elements-dom/article.md
@@ -55,7 +55,7 @@ Also, there's a global variable named by `id` that references the element:
```
```warn header="Please don't use id-named global variables to access elements"
-This behavior is described [in the specification](http://www.whatwg.org/specs/web-apps/current-work/#dom-window-nameditem), so it's a kind of standard. But it is supported mainly for compatibility.
+This behavior is described [in the specification](http://www.whatwg.org/specs/web-apps/current-work/#dom-window-nameditem), so it's kind of standard. But it is supported mainly for compatibility.
The browser tries to help us by mixing namespaces of JS and DOM. That's fine for simple scripts, inlined into HTML, but generally isn't a good thing. There may be naming conflicts. Also, when one reads JS code and doesn't have HTML in view, it's not obvious where the variable comes from.
@@ -71,7 +71,7 @@ If there are multiple elements with the same `id`, then the behavior of methods
```
```warn header="Only `document.getElementById`, not `anyElem.getElementById`"
-The method `getElementById` can be called only on `document` object. It looks for the given `id` in the whole document.
+The method `getElementById` that can be called only on `document` object. It looks for the given `id` in the whole document.
```
## querySelectorAll [#querySelectorAll]
@@ -103,7 +103,7 @@ Here we look for all `
` elements that are last children:
This method is indeed powerful, because any CSS selector can be used.
```smart header="Can use pseudo-classes as well"
-Pseudo-classes in the CSS selector like `:hover` and `:active` are also supported. For instance, `document.querySelectorAll(':hover')` will return the collection with elements that the pointer is over now (in nesting order: from the outermost `` to the most nested one).
+Pseudo-classes in the CSS selector like `:hover` and `:active` are also supported. For instance, `document.querySelectorAll(':hover')` will return the collection with elements that the pointer is over now (in nesting order: from the outermost `` to the most nested one).
```
## querySelector [#querySelector]
@@ -116,9 +116,9 @@ In other words, the result is the same as `elem.querySelectorAll(css)[0]`, but t
Previous methods were searching the DOM.
-The [elem.matches(css)](https://dom.spec.whatwg.org/#dom-element-matches) does not look for anything, it merely checks if `elem` matches the given CSS-selector. It returns `true` or `false`.
+The [elem.matches(css)](http://dom.spec.whatwg.org/#dom-element-matches) does not look for anything, it merely checks if `elem` matches the given CSS-selector. It returns `true` or `false`.
-The method comes in handy when we are iterating over elements (like in an array or something) and trying to filter out those that interest us.
+The method comes in handy when we are iterating over elements (like in array or something) and trying to filter those that interest us.
For instance:
@@ -142,7 +142,7 @@ For instance:
*Ancestors* of an element are: parent, the parent of parent, its parent and so on. The ancestors together form the chain of parents from the element to the top.
-The method `elem.closest(css)` looks for the nearest ancestor that matches the CSS-selector. The `elem` itself is also included in the search.
+The method `elem.closest(css)` looks the nearest ancestor that matches the CSS-selector. The `elem` itself is also included in the search.
In other words, the method `closest` goes up from the element and checks each of parents. If it matches the selector, then the search stops, and the ancestor is returned.
@@ -154,7 +154,7 @@ For instance:
Chapter 1
-
Chapter 2
+
Chapter 1
@@ -178,7 +178,7 @@ So here we cover them mainly for completeness, while you can still find them in
- `elem.getElementsByTagName(tag)` looks for elements with the given tag and returns the collection of them. The `tag` parameter can also be a star `"*"` for "any tags".
- `elem.getElementsByClassName(className)` returns elements that have the given CSS class.
-- `document.getElementsByName(name)` returns elements with the given `name` attribute, document-wide. Very rarely used.
+- `document.getElementsByName(name)` returns elements with the given `name` attribute, document-wide. very rarely used.
For instance:
```js
@@ -363,7 +363,7 @@ There are 6 main methods to search for nodes in DOM:
-By far the most used are `querySelector` and `querySelectorAll`, but `getElement(s)By*` can be sporadically helpful or found in the old scripts.
+By far the most used are `querySelector` and `querySelectorAll`, but `getElementBy*` can be sporadically helpful or found in the old scripts.
Besides that:
diff --git a/2-ui/1-document/05-basic-dom-node-properties/article.md b/2-ui/1-document/05-basic-dom-node-properties/article.md
index 99dde5bc..6c0053d0 100644
--- a/2-ui/1-document/05-basic-dom-node-properties/article.md
+++ b/2-ui/1-document/05-basic-dom-node-properties/article.md
@@ -10,7 +10,7 @@ Different DOM nodes may have different properties. For instance, an element node
Each DOM node belongs to the corresponding built-in class.
-The root of the hierarchy is [EventTarget](https://dom.spec.whatwg.org/#eventtarget), that is inherited by [Node](https://dom.spec.whatwg.org/#interface-node), and other DOM nodes inherit from it.
+The root of the hierarchy is [EventTarget](https://dom.spec.whatwg.org/#eventtarget), that is inherited by [Node](http://dom.spec.whatwg.org/#interface-node), and other DOM nodes inherit from it.
Here's the picture, explanations to follow:
@@ -18,39 +18,16 @@ Here's the picture, explanations to follow:
The classes are:
-- [EventTarget](https://dom.spec.whatwg.org/#eventtarget) -- is the root "abstract" class for everything.
-
- Objects of that class are never created. It serves as a base, so that all DOM nodes support so-called "events", we'll study them later.
-
-- [Node](https://dom.spec.whatwg.org/#interface-node) -- is also an "abstract" class, serving as a base for DOM nodes.
-
- It provides the core tree functionality: `parentNode`, `nextSibling`, `childNodes` and so on (they are getters). Objects of `Node` class are never created. But there are other classes that inherit from it (and so inherit the `Node` functionality).
-
-- [Document](https://dom.spec.whatwg.org/#interface-document), for historical reasons often inherited by `HTMLDocument` (though the latest spec doesn't dictate it) -- is a document as a whole.
-
- The `document` global object belongs exactly to this class. It serves as an entry point to the DOM.
-
-- [CharacterData](https://dom.spec.whatwg.org/#interface-characterdata) -- an "abstract" class, inherited by:
- - [Text](https://dom.spec.whatwg.org/#interface-text) -- the class corresponding to a text inside elements, e.g. `Hello` in `