-
-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..5fc7743f 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/4-function-property-after-bind/task.md b/1-js/06-advanced-functions/10-bind/4-function-property-after-bind/task.md
index d6cfb44b..8cd18ec5 100644
--- a/1-js/06-advanced-functions/10-bind/4-function-property-after-bind/task.md
+++ b/1-js/06-advanced-functions/10-bind/4-function-property-after-bind/task.md
@@ -4,7 +4,7 @@ importance: 5
# Function property after bind
-There's a value in the property of a function. Will it change after `bind`? Why, or why not?
+There's a value in the property of a function. Will it change after `bind`? Why, elaborate?
```js run
function sayHi() {
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..ce0c94a5 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:
@@ -267,7 +258,7 @@ alert( triple(5) ); // = mul(3, 5) = 15
Why do we usually make a partial function?
-The benefit is that we can create an independent function with a readable name (`double`, `triple`). We can use it and not provide the first argument every time as it's fixed with `bind`.
+The benefit is that we can create an independent function with a readable name (`double`, `triple`). We can use it and not provide first argument of every time as it's fixed with `bind`.
In other cases, partial application is useful when we have a very generic function and want a less universal variant of it for convenience.
@@ -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..abc5dd80 100644
--- a/1-js/06-advanced-functions/12-arrow-functions/article.md
+++ b/1-js/06-advanced-functions/12-arrow-functions/article.md
@@ -4,7 +4,7 @@ Let's revisit arrow functions.
Arrow functions are not just a "shorthand" for writing small stuff. They have some very specific and useful features.
-JavaScript is full of situations where we need to write a small function that's executed somewhere else.
+JavaScript is full of situations where we need to write a small function, that's executed somewhere else.
For instance:
@@ -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..11e14221 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,23 @@ 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..b3bc2ec0 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
@@ -27,7 +27,7 @@ The getter works when `obj.propName` is read, the setter -- when it is assigned.
For instance, we have a `user` object with `name` and `surname`:
-```js
+```js run
let user = {
name: "John",
surname: "Smith"
@@ -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,13 +94,17 @@ 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
-Descriptors for accessor properties are different from those for data properties.
+Descriptors for accessor properties are different -- as compared with data properties.
-For accessor properties, there is no `value` or `writable`, but instead there are `get` and `set` functions.
+For accessor properties, there is no `value` and `writable`, but instead there are `get` and `set` functions.
That is, an accessor descriptor may have:
@@ -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/solution.md b/1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/solution.md
index c141b2ec..bd412f12 100644
--- a/1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/solution.md
+++ b/1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/solution.md
@@ -44,7 +44,7 @@ alert( lazy.stomach ); //
Now all works fine, because `this.stomach=` does not perform a lookup of `stomach`. The value is written directly into `this` object.
-Also we can totally avoid the problem by making sure that each hamster has their own stomach:
+Also we can totally evade the problem by making sure that each hamster has their own stomach:
```js run
let hamster = {
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..5895a0b3 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,15 +27,23 @@ 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:
-```js
+```js run
let animal = {
eats: true
};
@@ -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.
@@ -220,7 +211,7 @@ The answer is simple: `this` is not affected by prototypes at all.
So, the setter call `admin.fullName=` uses `admin` as `this`, not `user`.
-That is actually a super-important thing, because we may have a big object with many methods, and have objects that inherit from it. And when the inheriting objects run the inherited methods, they will modify only their own states, not the state of the big object.
+That is actually a super-important thing, because we may have a big object with many methods and inherit from it. Then inherited objects can run its methods, and they will modify the state of these objects, not the big one.
For instance, here `animal` represents a "method storage", and `rabbit` makes use of it.
@@ -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):
@@ -317,9 +308,9 @@ Here we have the following inheritance chain: `rabbit` inherits from `animal`, t
Note, there's one funny thing. Where is the method `rabbit.hasOwnProperty` coming from? We did not define it. Looking at the chain we can see that the method is provided by `Object.prototype.hasOwnProperty`. In other words, it's inherited.
-...But why does `hasOwnProperty` not appear in the `for..in` loop like `eats` and `jumps` do, if `for..in` lists inherited properties?
+...But why `hasOwnProperty` does not appear in `for..in` loop, like `eats` and `jumps`, if it lists all inherited properties.
-The answer is simple: it's not enumerable. Just like all other properties of `Object.prototype`, it has `enumerable:false` flag. And `for..in` only lists enumerable properties. That's why it and the rest of the `Object.prototype` properties are not listed.
+The answer is simple: it's not enumerable. Just like all other properties of `Object.prototype`, it has `enumerable:false` flag. That's why they are not listed.
```smart header="Almost all other key/value-getting methods ignore inherited properties"
Almost all other key/value-getting methods, such as `Object.keys`, `Object.values` and so on ignore inherited properties.
@@ -333,6 +324,6 @@ They only operate on the object itself. Properties from the prototype are *not*
- We can use `obj.__proto__` to access it (a historical getter/setter, there are other ways, to be covered soon).
- The object referenced by `[[Prototype]]` is called a "prototype".
- If we want to read a property of `obj` or call a method, and it doesn't exist, then JavaScript tries to find it in the prototype.
-- Write/delete operations act directly on the object, they don't use the prototype (assuming it's a data property, not a setter).
+- Write/delete operations for act directly on the object, they don't use the prototype (assuming it's a data property, not is a setter).
- If we call `obj.method()`, and the `method` is taken from the prototype, `this` still references `obj`. So methods always work with the current object even if they are inherited.
-- The `for..in` loop iterates over both its own and its inherited properties. All other key/value-getting methods only operate on the object itself.
+- The `for..in` loop iterates over both own and inherited properties. All other key/value-getting methods only operate on the object itself.
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..fe4c2cac 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..3e81f262 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..3cf5c4c7 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..acd42063 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..ebdef958 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..735e1f2b 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..433bc613 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..d32585b4 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..43190e16 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
@@ -15,7 +15,7 @@ alert( user2.name ); // Pete (worked!)
It worked, because `User.prototype.constructor == User`.
-..But if someone, so to speak, overwrites `User.prototype` and forgets to recreate `constructor` to reference `User`, then it would fail.
+..But if someone, so to say, overwrites `User.prototype` and forgets to recreate `"constructor"`, then it would fail.
For instance:
@@ -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 -- 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..35cdc61f 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..3489ecdd 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..3e11f275 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/2-defer-to-prototype-extended/solution.md b/1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/solution.md
index 99c358c9..e3651683 100644
--- a/1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/solution.md
+++ b/1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/solution.md
@@ -15,27 +15,3 @@ function f(a, b) {
f.defer(1000)(1, 2); // shows 3 after 1 sec
```
-
-Please note: we use `this` in `f.apply` to make our decoration work for object methods.
-
-So if the wrapper function is called as an object method, then `this` is passed to the original method `f`.
-
-```js run
-Function.prototype.defer = function(ms) {
- let f = this;
- return function(...args) {
- setTimeout(() => f.apply(this, args), ms);
- }
-};
-
-let user = {
- name: "John",
- sayHi() {
- alert(this.name);
- }
-}
-
-user.sayHi = user.sayHi.defer(1000);
-
-user.sayHi();
-```
diff --git a/1-js/08-prototypes/03-native-prototypes/article.md b/1-js/08-prototypes/03-native-prototypes/article.md
index bdfc86dd..66be00ca 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 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 cause 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..35cdc61f 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..770c908c 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..4989df56 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..38c33cae 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..858f8317 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..8d3d0bee 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..3489ecdd
--- /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..3e11f275 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..8a71dbf1 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,13 +22,13 @@ 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
*!*
-alert(Object.getPrototypeOf(rabbit) === animal); // true
+alert(Object.getPrototypeOf(rabbit) === animal); // get the prototype of rabbit
*/!*
*!*
@@ -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..858f8317 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..3cfcd4cb 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:
@@ -89,7 +89,7 @@ What `class User {...}` construct really does is:
1. Creates a function named `User`, that becomes the result of the class declaration. The function code is taken from the `constructor` method (assumed empty if we don't write such method).
2. Stores class methods, such as `sayHi`, in `User.prototype`.
-After `new User` object is created, when we call its method, it's taken from the prototype, just as described in the chapter . So the object has access to class methods.
+Afterwards, for `new User` objects, when we call a method, it's taken from the prototype, just as described in the chapter . So the object has access to class methods.
We can illustrate the result of `class User` declaration as:
@@ -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.
+Although, 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`:
+ 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,9 +229,9 @@ new User().sayHi(); // Hello
```
-## Getters/setters
+## Getters/setters, other shorthands
-Just like literal objects, classes may include getters/setters, computed properties etc.
+Just like literal objects, classes may include getters/setters, generators, computed properties etc.
Here's an example for `user.name` implemented using `get/set`:
@@ -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,20 @@ class User {
new User().sayHi();
```
-Such features are easy to remember, as they resemble that of literal objects.
+For a generator method, similarly, prepend it with `*`.
-## 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 +319,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 constructor, it's the property of the object itself.
## Summary
@@ -423,6 +346,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..5ac0146a 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 54%
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..0a1f4382 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 76%
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..fa26ec83 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).
+2. Between the constructor functions itself (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 88%
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..ca6628ed 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
---
@@ -19,6 +19,7 @@ let rabbit = new Rabbit("Rab");
*!*
// hasOwnProperty method is from Object.prototype
+// rabbit.__proto__ === Object.prototype
alert( rabbit.hasOwnProperty('name') ); // true
*/!*
```
@@ -38,5 +39,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..3412d982 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..6921698e 100644
--- a/1-js/09-classes/02-class-inheritance/article.md
+++ b/1-js/09-classes/02-class-inheritance/article.md
@@ -1,13 +1,9 @@
# Class inheritance
-Class inheritance is a way for one class to extend another class.
+Let's say we have two classes.
-So we can create new functionality on top of the existing.
-
-## The "extends" keyword
-
-Let's say we have class `Animal`:
+`Animal`:
```js
class Animal {
@@ -16,7 +12,7 @@ class Animal {
this.name = name;
}
run(speed) {
- this.speed = speed;
+ this.speed += speed;
alert(`${this.name} runs with speed ${this.speed}.`);
}
stop() {
@@ -28,19 +24,52 @@ class Animal {
let animal = new Animal("My animal");
```
-Here's how we can represent `animal` object and `Animal` class graphically:
-

-...And we would like to create another `class Rabbit`.
-As rabbits are animals, `Rabbit` class should be based on `Animal`, have access to animal methods, so that rabbits can do what "generic" animals can do.
-
-The syntax to extend another class is: `class Child extends Parent`.
-
-Let's create `class Rabbit` that inherits from `Animal`:
+...And `Rabbit`:
```js
+class Rabbit {
+ constructor(name) {
+ this.name = name;
+ }
+ hide() {
+ alert(`${this.name} hides!`);
+ }
+}
+
+let rabbit = new Rabbit("My rabbit");
+```
+
+
+
+
+Right now they are fully independent.
+
+But we'd want `Rabbit` to extend `Animal`. In other words, rabbits should be based on animals, have access to methods of `Animal` and extend them with its own methods.
+
+To inherit from another class, we should specify `"extends"` and the parent class before the braces `{..}`.
+
+Here `Rabbit` inherits from `Animal`:
+
+```js run
+class Animal {
+ constructor(name) {
+ this.speed = 0;
+ this.name = name;
+ }
+ run(speed) {
+ this.speed += speed;
+ alert(`${this.name} runs with speed ${this.speed}.`);
+ }
+ stop() {
+ this.speed = 0;
+ alert(`${this.name} stands still.`);
+ }
+}
+
+// Inherit from Animal by specifying "extends Animal"
*!*
class Rabbit extends Animal {
*/!*
@@ -55,18 +84,15 @@ 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()`.
+Now the `Rabbit` code became a bit shorter, as it uses `Animal` constructor by default, and it also can `run`, as animals do.
-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`.
+Internally, `extends` keyword adds `[[Prototype]]` reference from `Rabbit.prototype` to `Animal.prototype`:

-For instance, to find `rabbit.run` method, the engine checks (bottom-up on the picture):
-1. The `rabbit` object (has no `run`).
-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.
+So, if a method is not found in `Rabbit.prototype`, JavaScript takes it from `Animal.prototype`.
-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 uses prototypal inheritance for build-in objects. E.g. `Date.prototype.[[Prototype]]` is `Object.prototype`, so dates have 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 +102,8 @@ For instance, a function call that generates the parent class:
```js run
function f(phrase) {
return class {
- sayHi() { alert(phrase); }
- };
+ sayHi() { alert(phrase) }
+ }
}
*!*
@@ -93,20 +119,19 @@ That may be useful for advanced programming patterns when we use functions to ge
## Overriding a method
-Now let's move forward and override a method. By default, all methods that are not specified in `class Rabbit` are taken directly "as is" from `class Animal`.
+Now let's move forward and override a method. As of now, `Rabbit` inherits the `stop` method that sets `this.speed = 0` from `Animal`.
-But if we specify our own method in `Rabbit`, such as `stop()` then it will be used instead:
+If we specify our own `stop` in `Rabbit`, then it will be used instead:
```js
class Rabbit extends Animal {
stop() {
- // ...now this will be used for rabbit.stop()
- // instead of stop() from class Animal
+ // ...this will be used for rabbit.stop()
}
}
```
-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.
+...But usually we don't want to totally replace a parent method, but rather to build on top of it, 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 +149,7 @@ class Animal {
}
run(speed) {
- this.speed = speed;
+ this.speed += speed;
alert(`${this.name} runs with speed ${this.speed}.`);
}
@@ -151,7 +176,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 +185,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 +201,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 +255,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.
+Of course, there's an explanation. Let's get into details, so you'd 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 labelled 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 +303,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]]
@@ -382,7 +312,7 @@ If you're reading the tutorial for the first time - this section may be skipped.
It's about the internal mechanisms behind inheritance and `super`.
```
-Let's get a little deeper under the hood of `super`. We'll see some interesting things along the way.
+Let's get a little deeper under the hood of `super`. We'll see some interesting things by the way.
First to say, from all that we've learned till now, it's impossible for `super` to work at all!
@@ -533,7 +463,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 +472,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 +486,7 @@ let rabbit = {
let plant = {
sayHi() {
- alert("I'm a plant");
+ console.log("I'm a plant");
}
};
@@ -573,7 +503,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 +524,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 +554,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..546aa334 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..3bdda5a0 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..91f82896 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..bf86db77 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..8a5e2503 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..c9c8fea9 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..342574da 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..fab401df 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..583fabcf 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:
@@ -212,12 +192,12 @@ class Rabbit extends Animal {}
alert(Rabbit.__proto__ === Animal); // true
// for regular methods
-alert(Rabbit.prototype.__proto__ === Animal.prototype); // true
+alert(Rabbit.prototype.__proto__ === Animal.prototype);
```
## 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..ef0d497a 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;
}
}
@@ -254,7 +248,7 @@ Unlike protected ones, private fields are enforced by the language itself. That'
But if we inherit from `CoffeeMachine`, then we'll have no direct access to `#waterAmount`. We'll need to rely on `waterAmount` getter/setter:
```js
-class MegaCoffeeMachine extends CoffeeMachine {
+class MegaCoffeeMachine extends CoffeeMachine() {
method() {
*!*
alert( this.#waterAmount ); // Error: can only access from CoffeeMachine
@@ -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.
@@ -308,15 +302,15 @@ Supportable
For users, when a new version comes out, it may be a total overhaul internally, but still simple to upgrade if the external interface is the same.
Hiding complexity
-: People adore using things that are simple. At least from outside. What's inside is a different thing.
+: People adore to use things that are simple. At least from outside. What's inside is a different thing.
Programmers are not an exception.
**It's always convenient when implementation details are hidden, and a simple, well-documented external interface is available.**
-To hide 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..2dc4902b 100644
--- a/1-js/09-classes/05-extend-natives/article.md
+++ b/1-js/09-classes/05-extend-natives/article.md
@@ -21,7 +21,7 @@ alert(filteredArr); // 10, 50
alert(filteredArr.isEmpty()); // false
```
-Please note a very interesting thing. Built-in methods like `filter`, `map` and others -- return new objects of exactly the inherited type `PowerArray`. Their internal implementation uses the object's `constructor` property for that.
+Please note a very interesting thing. Built-in methods like `filter`, `map` and others -- return new objects of exactly the inherited type `PowerArray`. Their internal implementation uses object `constructor` property for that.
In the example above,
```js
@@ -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:
@@ -74,11 +74,11 @@ Built-in objects have their own static methods, for instance `Object.keys`, `Arr
As we already know, native classes extend each other. For instance, `Array` extends `Object`.
-Normally, when one class extends another, both static and non-static methods are inherited. That was thoroughly explained in the article [](info:static-properties-methods#statics-and-inheritance).
+Normally, when one class extends another, both static and non-static methods are inherited. That was thoroughly explained in the chapter [](info:static-properties-methods#statics-and-inheritance).
But built-in classes are an exception. They don't inherit statics from each other.
-For example, both `Array` and `Date` inherit from `Object`, so their instances have methods from `Object.prototype`. But `Array.[[Prototype]]` does not reference `Object`, so there's no, for instance, `Array.keys()` (or `Date.keys()`) static method.
+For example, both `Array` and `Date` inherit from `Object`, so their instances have methods from `Object.prototype`. But `Array.[[Prototype]]` does not reference `Object`, so there's no `Array.keys()` and `Date.keys()` static methods.
Here's the picture structure for `Date` and `Object`:
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..a0165bcc 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..0b02c99b 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]
@@ -44,9 +44,9 @@ alert( arr instanceof Array ); // true
alert( arr instanceof Object ); // true
```
-Please note that `arr` also belongs to the `Object` class. That's because `Array` prototypically inherits from `Object`.
+Please note that `arr` also belongs to the `Object` class. That's because `Array` prototypally 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!)
*/!*
@@ -105,9 +105,9 @@ Here's the illustration of what `rabbit instanceof Animal` compares with `Animal
By the way, there's also a method [objA.isPrototypeOf(objB)](mdn:js/object/isPrototypeOf), that returns `true` if `objA` is somewhere in the chain of prototypes for `objB`. So the test of `obj instanceof Class` can be rephrased as `Class.prototype.isPrototypeOf(obj)`.
-It's funny, but the `Class` constructor itself does not participate in the check! Only the chain of prototypes and `Class.prototype` matters.
+That'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..920be68b 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..d5f1ab83 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,21 +101,21 @@ 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
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.
+An important feature of many browser objects (for instance) is that they can generate events. Events is a great way to "broadcast information" to anyone who wants it. So let's make a mixin that allows 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.
-- ...And the method `.off(name, handler)` that removes the `handler` listener.
+- 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 `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.
+After adding the mixin, an object `user` will become able to generate an event `"login"` when the visitor logs in. And another object, say, `calendar` may want to listen to such events to load the calendar for the logged-in person.
Or, a `menu` can generate the event `"select"` when a menu item is selected, and other objects may assign handlers to react on that event. And so on.
@@ -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
}
@@ -165,7 +165,7 @@ let eventMixin = {
```
-- `.on(eventName, handler)` -- assigns function `handler` to run when the event with that name occurs. Technically, there's an `_eventHandlers` property that stores an array of handlers for each event name, and it just adds it to the list.
+- `.on(eventName, handler)` -- assigns function `handler` to run when the event with that name happens. Technically, there's `_eventHandlers` property, that stores an array of handlers for each event name. So it just adds it to the list.
- `.off(eventName, handler)` -- removes the function from the handlers list.
- `.trigger(eventName, ...args)` -- generates the event: all handlers from `_eventHandlers[eventName]` are called, with a list of arguments `...args`.
@@ -193,7 +193,7 @@ menu.on("select", value => alert(`Value selected: ${value}`));
menu.choose("123");
```
-Now, if we'd like any code to react to a menu selection, we can listen for it with `menu.on(...)`.
+Now if we'd like any code to react on menu selection, we can listen to it with `menu.on(...)`.
And `eventMixin` mixin makes it easy to add such behavior to as many classes as we'd like, without interfering with the inheritance chain.
@@ -201,8 +201,8 @@ And `eventMixin` mixin makes it easy to add such behavior to as many classes as
*Mixin* -- is a generic object-oriented programming term: a class that contains methods for other classes.
-Some other languages allow multiple inheritance. JavaScript does not support multiple inheritance, but mixins can be implemented by copying methods into prototype.
+Some other languages like allow multiple inheritance. JavaScript does not support multiple inheritance, but mixins can be implemented by copying methods into prototype.
-We can use mixins as a way to augment a class by adding multiple behaviors, like event-handling as we have seen above.
+We can use mixins as a way to augment a class by multiple behaviors, like event-handling as we have seen above.
-Mixins may become a point of conflict if they accidentally overwrite existing class methods. So generally one should think well about the naming methods of a mixin, to minimize the probability of that happening.
+Mixins may become a point of conflict if they accidentally overwrite existing class methods. So generally one should think well about the naming methods of a mixin, to minimize the probability of that.
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..2e7ba8b3 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..fa8ba5e9 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.
+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 of 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) contains an error object with details about what's 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.
+The JavaScript engine first reads the code, and then runs it. The errors that occur on the reading phrase 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 {
@@ -526,19 +522,19 @@ alert(result || "error occurred");
alert( `execution took ${diff}ms` );
```
-You can check by running the code with entering `35` into `prompt` -- it executes normally, `finally` after `try`. And then enter `-1` -- there will be an immediate error, and the execution will take `0ms`. Both measurements are done correctly.
+You can check by running the code with entering `35` into `prompt` -- it executes normally, `finally` after `try`. And then enter `-1` -- there will be an immediate error, an the execution will take `0ms`. Both measurements are done correctly.
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 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..dd15063d 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..2414ce7e 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, here's it approximate code, for us to 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` in the `catch` block, that check the class and handle known errors and rethrow the unknown ones. But if `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 wants to have some kind of "data reading error". Why exactly it happened -- is often irrelevant (the error message describes it). Or, even better if there is a way to get 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 way wait for the document to be processed. The regular scripts runs immediately, so we saw its output first.
-When using modules, we should be aware that 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
@@ -62,36 +56,36 @@ On the picture above, you can click on element nodes and their children will ope
Every tree node is an object.
-Tags are *element nodes* (or just elements) and form the tree structure: `` is at the root, then `` and `` are its children, etc.
+Tags are *element nodes* (or just elements), they form the tree structure: `` is at the root, then `` and `` are its children, etc.
The text inside elements forms *text nodes*, labelled as `#text`. A text node contains only a string. It may not have children and is always a leaf of the tree.
-For instance, the `` tag has the text `"About elk"`.
+For instance, the `` tag has the text `"About elks"`.
Please note the special characters in text nodes:
- a newline: `↵` (in JavaScript known as `\n`)
- a space: `␣`
-Spaces and newlines are totally valid characters, like letters and digits. They form text nodes and become a part of the DOM. So, for instance, in the example above the `` tag contains some spaces before ``, and that text becomes a `#text` node (it contains a newline and some spaces only).
+Spaces and newlines -- are totally valid characters, like letters and digits. They form text nodes and become a part of the DOM. So, for instance, in the example above the `` tag contains some spaces before ``, and that text becomes a `#text` node (it contains a newline and some spaces only).
There are only two top-level exclusions:
-1. Spaces and newlines before `` are ignored for historical reasons.
-2. If we put something after ``, then that is automatically moved inside the `body`, at the end, as the HTML spec requires that all content must be inside ``. So there can't be any spaces after ``.
+1. Spaces and newlines before `` are ignored for historical reasons,
+2. If we put something after ``, then that is automatically moved inside the `body`, at the end, as the HTML spec requires that all content must be inside ``. So there may be no spaces after ``.
-In other cases everything's straightforward -- if there are spaces (just like any character) in the document, then they become text nodes in the DOM, and if we remove them, then there won't be any.
+In other cases everything's straightforward -- if there are spaces (just like any character) in the document, then they become text nodes in DOM, and if we remove them, then there won't be any.
Here are no space-only text nodes:
```html no-beautify
-About elkThe truth about elk.
+About elksThe truth about elks.
```
@@ -106,11 +100,11 @@ On further DOM pictures we'll sometimes omit them when they are irrelevant. Such
## Autocorrection
-If the browser encounters malformed HTML, it automatically corrects it when making the DOM.
+If the browser encounters malformed HTML, it automatically corrects it when making DOM.
-For instance, the top tag is always ``. Even if it doesn't exist in the document, it will exist in the DOM, because the browser will create it. The same goes for ``.
+For instance, the top tag is always ``. Even if it doesn't exist in the document -- it will exist in the DOM, the browser will create it. The same goes for ``.
-As an example, if the HTML file is the single word `"Hello"`, the browser will wrap it into `` and ``, and add the required ``, and the DOM will be:
+As an example, if the HTML file is a single word `"Hello"`, the browser will wrap it into `` and ``, add the required ``, and the DOM will be:
@@ -123,7 +117,7 @@ drawHtmlTree(node3, 'div.domtree', 690, 150);
While generating the DOM, browsers automatically process errors in the document, close tags and so on.
-A document with unclosed tags:
+Such document with unclosed tags:
```html no-beautify
Hello
@@ -132,7 +126,7 @@ A document with unclosed tags:
Dad
```
-...will become a normal DOM as the browser reads tags and restores the missing parts:
+...Will become a normal DOM, as the browser reads tags and restores the missing parts:
@@ -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 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
@@ -173,7 +167,7 @@ For example, comments:
- The truth about elk.
+ The truth about elks.
An elk is a smart
*!*
@@ -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.
@@ -208,29 +202,29 @@ There are [12 node types](https://dom.spec.whatwg.org/#node). In practice we usu
1. `document` -- the "entry point" into DOM.
2. element nodes -- HTML-tags, the tree building blocks.
3. text nodes -- contain text.
-4. comments -- sometimes we can put information there, it won't be shown, but JS can read it from the DOM.
+4. comments -- sometimes we can put the information there, it won't be shown, but JS can read it from the DOM.
## See it for yourself
-To see the DOM structure in real-time, try [Live DOM Viewer](http://software.hixie.ch/utilities/js/live-dom-viewer/). Just type in the document, and it will show up as a DOM at an instant.
+To see the DOM structure in real-time, try [Live DOM Viewer](http://software.hixie.ch/utilities/js/live-dom-viewer/). Just type in the document, and it will show up DOM at an instant.
Another way to explore the DOM is to use the browser developer tools. Actually, that's what we use when developing.
-To do so, open the web page [elk.html](elk.html), turn on the browser developer tools and switch to the Elements tab.
+To do so, open the web-page [elks.html](elks.html), turn on the browser developer tools and switch to the Elements tab.
It should look like this:
-
+
You can see the DOM, click on elements, see their details and so on.
Please note that the DOM structure in developer tools is simplified. Text nodes are shown just as text. And there are no "blank" (space only) text nodes at all. That's fine, because most of the time we are interested in element nodes.
-Clicking the button in the left-upper corner allows us to choose a node from the webpage using a mouse (or other pointer devices) and "inspect" it (scroll to it in the Elements tab). This works great when we have a huge HTML page (and corresponding huge DOM) and would like to see the place of a particular element in it.
+Clicking the button in the left-upper corner allows to choose a node from the webpage using a mouse (or other pointer devices) and "inspect" it (scroll to it in the Elements tab). This works great when we have a huge HTML page (and corresponding huge DOM) and would like to see the place of a particular element in it.
Another way to do it would be just right-clicking on a webpage and selecting "Inspect" in the context menu.
-
+
At the right part of the tools there are the following subtabs:
- **Styles** -- we can see CSS applied to the current element rule by rule, including built-in rules (gray). Almost everything can be edited in-place, including the dimensions/margins/paddings of the box below.
@@ -253,15 +247,15 @@ Now the last selected element is available as `$0`, the previously selected is `
We can run commands on them. For instance, `$0.style.background = 'red'` makes the selected list item red, like this:
-
+
That's how to get a node from Elements in Console.
There's also a road back. If there's a variable referencing a DOM node, then we can use the command `inspect(node)` in Console to see it in the Elements pane.
-Or we can just output the DOM node in the console and explore "in-place", like `document.body` below:
+Or we can just output DOM-node in the console and explore "at-place", like `document.body` below:
-
+
That's for debugging purposes of course. From the next chapter on we'll access and modify DOM using JavaScript.
@@ -279,4 +273,4 @@ We can use developer tools to inspect DOM and modify it manually.
Here we covered the basics, the most used and important actions to start with. There's an extensive documentation about Chrome Developer Tools at . The best way to learn the tools is to click here and there, read menus: most options are obvious. Later, when you know them in general, read the docs and pick up the rest.
-DOM nodes have properties and methods that allow us to travel between them, modify them, move around the page, and more. We'll get down to them in the next chapters.
+DOM nodes have properties and methods that allow to travel between them, modify, move around the page and more. We'll get down to them in the next chapters.
diff --git a/2-ui/1-document/02-dom-nodes/domconsole0.png b/2-ui/1-document/02-dom-nodes/domconsole0.png
new file mode 100644
index 00000000..121c11d7
Binary files /dev/null and b/2-ui/1-document/02-dom-nodes/domconsole0.png differ
diff --git a/2-ui/1-document/02-dom-nodes/domconsole0.svg b/2-ui/1-document/02-dom-nodes/domconsole0.svg
deleted file mode 100644
index eb99f193..00000000
--- a/2-ui/1-document/02-dom-nodes/domconsole0.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/2-ui/1-document/02-dom-nodes/domconsole0@2x.png b/2-ui/1-document/02-dom-nodes/domconsole0@2x.png
new file mode 100644
index 00000000..a8953395
Binary files /dev/null and b/2-ui/1-document/02-dom-nodes/domconsole0@2x.png differ
diff --git a/2-ui/1-document/02-dom-nodes/domconsole1.png b/2-ui/1-document/02-dom-nodes/domconsole1.png
new file mode 100644
index 00000000..c04f015c
Binary files /dev/null and b/2-ui/1-document/02-dom-nodes/domconsole1.png differ
diff --git a/2-ui/1-document/02-dom-nodes/domconsole1.svg b/2-ui/1-document/02-dom-nodes/domconsole1.svg
deleted file mode 100644
index 02ef5f0a..00000000
--- a/2-ui/1-document/02-dom-nodes/domconsole1.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/2-ui/1-document/02-dom-nodes/domconsole1@2x.png b/2-ui/1-document/02-dom-nodes/domconsole1@2x.png
new file mode 100644
index 00000000..ce0fa0ff
Binary files /dev/null and b/2-ui/1-document/02-dom-nodes/domconsole1@2x.png differ
diff --git a/2-ui/1-document/02-dom-nodes/elk.svg b/2-ui/1-document/02-dom-nodes/elk.svg
deleted file mode 100644
index 448eea9d..00000000
--- a/2-ui/1-document/02-dom-nodes/elk.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/2-ui/1-document/02-dom-nodes/elk.html b/2-ui/1-document/02-dom-nodes/elks.html
similarity index 86%
rename from 2-ui/1-document/02-dom-nodes/elk.html
rename to 2-ui/1-document/02-dom-nodes/elks.html
index dc5d65f5..7d29f3d4 100644
--- a/2-ui/1-document/02-dom-nodes/elk.html
+++ b/2-ui/1-document/02-dom-nodes/elks.html
@@ -1,7 +1,7 @@
- The truth about elk.
+ The truth about elks.
An elk is a smart
diff --git a/2-ui/1-document/02-dom-nodes/elks.png b/2-ui/1-document/02-dom-nodes/elks.png
new file mode 100644
index 00000000..03177c40
Binary files /dev/null and b/2-ui/1-document/02-dom-nodes/elks.png differ
diff --git a/2-ui/1-document/02-dom-nodes/elks@2x.png b/2-ui/1-document/02-dom-nodes/elks@2x.png
new file mode 100644
index 00000000..e8a15bd5
Binary files /dev/null and b/2-ui/1-document/02-dom-nodes/elks@2x.png differ
diff --git a/2-ui/1-document/02-dom-nodes/inspect.png b/2-ui/1-document/02-dom-nodes/inspect.png
new file mode 100644
index 00000000..075cf930
Binary files /dev/null and b/2-ui/1-document/02-dom-nodes/inspect.png differ
diff --git a/2-ui/1-document/02-dom-nodes/inspect.svg b/2-ui/1-document/02-dom-nodes/inspect.svg
deleted file mode 100644
index 60696ec0..00000000
--- a/2-ui/1-document/02-dom-nodes/inspect.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/2-ui/1-document/02-dom-nodes/inspect@2x.png b/2-ui/1-document/02-dom-nodes/inspect@2x.png
new file mode 100644
index 00000000..8743dd29
Binary files /dev/null and b/2-ui/1-document/02-dom-nodes/inspect@2x.png differ
diff --git a/2-ui/1-document/03-dom-navigation/article.md b/2-ui/1-document/03-dom-navigation/article.md
index b5f03098..3e327b47 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"
@@ -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:
```
@@ -321,9 +320,9 @@ The specification: [tabular data](https://html.spec.whatwg.org/multipage/tables.
There are also additional navigation properties for HTML forms. We'll look at them later when we start working with forms.
-## Summary
+# 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..20da46c1 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..fccbff1d 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..cd988f73 100644
--- a/2-ui/1-document/04-searching-elements-dom/article.md
+++ b/2-ui/1-document/04-searching-elements-dom/article.md
@@ -6,7 +6,40 @@ There are additional searching methods for that.
## document.getElementById or just id
-If an element has the `id` attribute, we can get the element using the method `document.getElementById(id)`, no matter where it is.
+If an element has the `id` attribute, then there's a global variable by the name from that `id`.
+
+We can use it to immediately access the element no matter where it is:
+
+```html run
+
+
Element
+
+
+
+```
+
+The behavior is described [in the specification](http://www.whatwg.org/specs/web-apps/current-work/#dom-window-nameditem), but it is supported mainly for compatibility. The browser tries to help us by mixing namespaces of JS and DOM. Good for very simple scripts, but there may be name conflicts. Also, when we look in JS and don't have HTML in view, it's not obvious where the variable comes from.
+
+If we declare a variable with the same name, it takes precedence:
+
+```html run untrusted height=0
+
+
+
+```
+
+The better alternative is to use a special method `document.getElementById(id)`.
For instance:
@@ -16,62 +49,24 @@ For instance:
```
-Also, there's a global variable named by `id` that references the element:
+Here in the tutorial we'll often use `id` to directly reference an element, but that's only to keep things short. In real life `document.getElementById` is the preferred method.
-```html run
-
-
Element
-
-
-
-```
-
-...That's unless we declare a JavaScript variable with the same name, then it takes precedence:
-
-```html run untrusted height=0
-
-
-
-```
-
-```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.
-
-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.
-
-Here in the tutorial we use `id` to directly reference an element for brevity, when it's obvious where the element comes from.
-
-In real life `document.getElementById` is the preferred method.
-```
-
-```smart header="The `id` must be unique"
+```smart header="There can be only one"
The `id` must be unique. There can be only one element in the document with the given `id`.
-If there are multiple elements with the same `id`, then the behavior of methods that use it is unpredictable, e.g. `document.getElementById` may return any of such elements at random. So please stick to the rule and keep `id` unique.
+If there are multiple elements with the same `id`, then the behavior of corresponding methods is unpredictable. The browser may return any of them at random. So please stick to the rule and keep `id` unique.
```
-```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.
+```warn header="Only `document.getElementById`, not `anyNode.getElementById`"
+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 +98,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 +111,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 +137,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 +149,7 @@ For instance:
Chapter 1
-
Chapter 2
+
Chapter 1
@@ -178,7 +173,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 +358,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 `