commit
e6a2f98c9d
19 changed files with 68 additions and 67 deletions
|
@ -19,7 +19,7 @@ To create a new `Date` object call `new Date()` with one of the following argume
|
|||
```
|
||||
|
||||
`new Date(milliseconds)`
|
||||
: Create a `Date` obeject with the time equal to number of milliseconds (1/1000 of a second) passed after the Jan 1st of 1970 UTC+0.
|
||||
: Create a `Date` object with the time equal to number of milliseconds (1/1000 of a second) passed after the Jan 1st of 1970 UTC+0.
|
||||
|
||||
```js run
|
||||
// 0 means 01.01.1970 UTC+0
|
||||
|
@ -70,7 +70,7 @@ To create a new `Date` object call `new Date()` with one of the following argume
|
|||
|
||||
## Access date components
|
||||
|
||||
The are many methods to access the year, month and so on from the `Date` object. But they can be easily remembered when categorized.
|
||||
There are many methods to access the year, month and so on from the `Date` object. But they can be easily remembered when categorized.
|
||||
|
||||
`getFullYear()`
|
||||
: Get the year (4 digits)
|
||||
|
@ -116,7 +116,7 @@ Besides the given methods, there are two special ones, that do not have a UTC-va
|
|||
: Returns the timestamp for the date -- a number of milliseconds passed from the January 1st of 1970 UTC+0.
|
||||
|
||||
`getTimezoneOffset()`
|
||||
: Returns the difference between the local time zene and UTC, in minutes:
|
||||
: Returns the difference between the local time zone and UTC, in minutes:
|
||||
|
||||
```js run
|
||||
// if you are in timezone UTC-1, outputs 60
|
||||
|
@ -227,7 +227,7 @@ alert( `The loop took ${end - start} ms` );
|
|||
|
||||
## Date.now()
|
||||
|
||||
If we only want to measure the difference, we don't need `Date` object.
|
||||
If we only want to measure the difference, we don't need the `Date` object.
|
||||
|
||||
There's a special method `Date.now()` that returns the current timestamp.
|
||||
|
||||
|
@ -403,10 +403,10 @@ alert(date);
|
|||
## Summary
|
||||
|
||||
- Date and time in JavaScript are represented with the [Date](mdn:js/Date) object. We can't create "only date" or "only time": `Date` objects always carry both.
|
||||
- Months are counted from the zero (yes, January is a zero month).
|
||||
- Days of week in `getDay()` are also counted from the zero (that's Sunday).
|
||||
- Months are counted from zero (yes, January is a zero month).
|
||||
- Days of week in `getDay()` are also counted from zero (that's Sunday).
|
||||
- `Date` auto-corrects itself when out-of-range components are set. Good for adding/substracting days/months/hours.
|
||||
- Dates can be substructed, giving their difference in milliseconds. That's because a `Date` becomes the timestamp if converted to a number.
|
||||
- Dates can be substracted, giving their difference in milliseconds. That's because a `Date` becomes the timestamp if converted to a number.
|
||||
- Use `Date.now()` to get the current timestamp fast.
|
||||
|
||||
Note that unlike many other systems, timestamps in JavaScript are in milliseconds, not in seconds.
|
||||
|
|
|
@ -21,7 +21,7 @@ let user = {
|
|||
alert(user); // {name: "John", age: 30}
|
||||
```
|
||||
|
||||
...But in the process of development, new properties are added, old properties are renamed and removed. Updating such `toString` every time can become a pain. We could try to loop over properties in it, but what is the object is complex and has nested objects in properties? We'd need to implement their conversion as well. And, if we're sending the object over a network, then we also need to supply the code to "read" our object on the receiving side.
|
||||
...But in the process of development, new properties are added, old properties are renamed and removed. Updating such `toString` every time can become a pain. We could try to loop over properties in it, but what if the object is complex and has nested objects in properties? We'd need to implement their conversion as well. And, if we're sending the object over a network, then we also need to supply the code to "read" our object on the receiving side.
|
||||
|
||||
Luckily, there's no need to write the code to handle all this. The task has been solved already.
|
||||
|
||||
|
@ -68,7 +68,7 @@ alert(json);
|
|||
|
||||
The method `JSON.stringify(student)` takes the object and converts it into a string.
|
||||
|
||||
The resulting `json` string is a called *JSON-encoded* or *serialized* or *stringified* object. We are ready to send it over the wire or put into plain data store.
|
||||
The resulting `json` string is a called *JSON-encoded* or *serialized* or *stringified* or *marshalled* object. We are ready to send it over the wire or put into plain data store.
|
||||
|
||||
|
||||
Please note that JSON-encoded object has several important differences from the object literal:
|
||||
|
@ -185,6 +185,7 @@ let json = JSON.stringify(value[, replacer, space])
|
|||
|
||||
value
|
||||
: A value to encode.
|
||||
|
||||
replacer
|
||||
: Array of properties to encode or a mapping function `function(key, value)`.
|
||||
|
||||
|
@ -478,9 +479,9 @@ alert( meetup.date.getDate() ); // Error!
|
|||
|
||||
Whoops! An error!
|
||||
|
||||
The value of `meetup.date` is a string, not a `Date` object. How `JSON.parse` may know that it should transform that string into a `Date`?
|
||||
The value of `meetup.date` is a string, not a `Date` object. How could `JSON.parse` know that it should transform that string into a `Date`?
|
||||
|
||||
Let's pass to `JSON.parse` the reviving function that returns all values "as is", but `date` wll become a `Date`:
|
||||
Let's pass to `JSON.parse` the reviving function that returns all values "as is", but `date` will become a `Date`:
|
||||
|
||||
```js run
|
||||
let str = '{"title":"Conference","date":"2017-11-30T12:00:00.000Z"}';
|
||||
|
|
|
@ -96,9 +96,9 @@ function pow(x, n) {
|
|||
```
|
||||
````
|
||||
|
||||
The maximal number of nested calls (including the first one) is called *recursion depth*. In our case, it there will be exactly `n`.
|
||||
The maximal number of nested calls (including the first one) is called *recursion depth*. In our case, it will be exactly `n`.
|
||||
|
||||
The maximal recursion depth is limited by JavaScript engine. We can make sure about 10000, some engines allow more, but 100000 is probably out of limit for the majority of them. There are automatic optimizations that help to alleviate this ("tail calls optimizations"), but they are not yet supported everywhere and work only in simple cases.
|
||||
The maximal recursion depth is limited by JavaScript engine. We can make sure about 10000, some engines allow more, but 100000 is probably out of limit for the majority of them. There are automatic optimizations that help alleviate this ("tail calls optimizations"), but they are not yet supported everywhere and work only in simple cases.
|
||||
|
||||
That limits the application of recursion, but it still remains very wide. There are many tasks where recursive way of thinking gives simpler code, easier to maintain.
|
||||
|
||||
|
@ -230,7 +230,7 @@ function pow(x, n) {
|
|||
|
||||
There are no more nested calls, so the function finishes, returning `2`.
|
||||
|
||||
As the function finishes, its execution context is not needed any more, so it's removed from the memory. The previous one is restored off-top of the stack:
|
||||
As the function finishes, its execution context is not needed any more, so it's removed from the memory. The previous one is restored off the top of the stack:
|
||||
|
||||
|
||||
<ul class="function-execution-context-list">
|
||||
|
|
|
@ -102,7 +102,7 @@ In old times, rest parameters did not exist in the language, and `arguments` was
|
|||
|
||||
And it still works, we can use it.
|
||||
|
||||
But the downside is that though `arguments` is both array-like and iterable, it's not an array. It does not support array methods, so we can't say call `arguments.map(...)`.
|
||||
But the downside is that although `arguments` is both array-like and iterable, it's not an array. It does not support array methods, so we can't say call `arguments.map(...)`.
|
||||
|
||||
Also, it always has all arguments in it, we can't capture them partially, like we did with rest parameters.
|
||||
|
||||
|
@ -121,7 +121,7 @@ function f() {
|
|||
|
||||
f(1); // 1
|
||||
```
|
||||
As we remember, arrow functions don't have own `this`. Now we know they don't have the special `arguments` object too.
|
||||
As we remember, arrow functions don't have their own `this`. Now we know they don't have the special `arguments` object either.
|
||||
|
||||
````
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ importance: 5
|
|||
|
||||
# Filter through function
|
||||
|
||||
We have a built-in method `arr.filter(f)` for arrays. It filters all elements through the function `f`. If it returns `true`, then such an element is returned in the resulting array.
|
||||
We have a built-in method `arr.filter(f)` for arrays. It filters all elements through the function `f`. If it returns `true`, then that element is returned in the resulting array.
|
||||
|
||||
Make a set of "ready to use" filters:
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ users.sort((a, b) => a.name > b.name ? 1 : -1);
|
|||
users.sort((a, b) => a.age > b.age ? 1 : -1);
|
||||
```
|
||||
|
||||
Can we make it even more verbose, like this?
|
||||
Can we make it even less verbose, like this?
|
||||
|
||||
```js
|
||||
users.sort(byField('name'));
|
||||
|
|
|
@ -70,7 +70,7 @@ In JavaScript, every running function, code block and the script as a whole have
|
|||
The Lexical Environment object consists of two parts:
|
||||
|
||||
1. *Environment Record* -- an object that has all local variables as its properties (and some other information like the value of `this`).
|
||||
2. An reference to the *outer lexical environment*, usually the one associated with the code lexically right outside of it (outside of the current figure brackets).
|
||||
2. A reference to the *outer lexical environment*, usually the one associated with the code lexically right outside of it (outside of the current figure brackets).a
|
||||
|
||||
So, a "variable" is just a property of the special internal object, Environment Record. "To get or change a variable" means "to get or change the property of that object".
|
||||
|
||||
|
@ -80,18 +80,18 @@ For instance, in this simple code, there is only one Lexical Environment:
|
|||
|
||||
This is a so-called global Lexical Environment, associated with the whole script. For browsers, all `<script>` tags share the same global environment.
|
||||
|
||||
On the picture above the rectangle means Environment Record (variable store) and the arrow means the outer reference. The global Lexical Environment has no outer one, so that's `null`.
|
||||
On the picture above, the rectangle means Environment Record (variable store) and the arrow means the outer reference. The global Lexical Environment has no outer one, so that's `null`.
|
||||
|
||||
Here's the bigger picture of how `let` variables work:
|
||||
|
||||

|
||||
|
||||
Rectangles at the right side demonstrate how the global Lexical Environment changes during the execution:
|
||||
Rectangles ont the right-hand side demonstrate how the global Lexical Environment changes during the execution:
|
||||
|
||||
1. When the script starts, the Lexical Environment is empty.
|
||||
2. The `let phrase` definition appears. Now it initially has no value, so `undefined` is stored.
|
||||
3. The `phrase` is assigned.
|
||||
4. The `phrase` changes the value.
|
||||
3. `phrase` is assigned.
|
||||
4. `phrase` refers to a new value.
|
||||
|
||||
Everything looks simple for now, right?
|
||||
|
||||
|
@ -135,7 +135,7 @@ Here's the picture of Lexical Environments when the execution is inside `say("Jo
|
|||
|
||||
During the function call we have two Lexical Environments: the inner one (for the function call) and the outer one (global):
|
||||
|
||||
- The inner Lexical Environment corresponds to the current `say` execution. It has a single variable: `name`, the function argument. We called `say("John")`, so the value of `name` is `"John"`.
|
||||
- The inner Lexical Environment corresponds to the current execution of `say`. It has a single variable: `name`, the function argument. We called `say("John")`, so the value of `name` is `"John"`.
|
||||
- The outer Lexical Environment is the global Lexical Environment.
|
||||
|
||||
The inner Lexical Environment one has the `outer` reference to the outer one.
|
||||
|
@ -254,7 +254,7 @@ alert( counter() ); // 2
|
|||
|
||||
Let's go on with the `makeCounter` example. It creates the "counter" function that returns the next number on each invocation. Despite being simple, slightly modified variants of that code have practical uses, for instance, as a [pseudorandom number generator](https://en.wikipedia.org/wiki/Pseudorandom_number_generator), and more. So the example is not exactly "artificial".
|
||||
|
||||
How the counter works?
|
||||
How does the counter work?
|
||||
|
||||
When the inner function runs, the variable in `count++` is searched from inside out.
|
||||
|
||||
|
@ -358,7 +358,7 @@ For a more in-depth understanding, here's what's going on in the `makeCounter` e
|
|||
|
||||
Generally, a Lexical Environment object lives until there is a function which may use it. And when there are none, it is cleared.
|
||||
|
||||
6. The call to `counter()` not only returns the value of `count`, but also increases it. Note that the modification is done "at place". The value of `count` is modified exactly in the environment where it was found.
|
||||
6. The call to `counter()` not only returns the value of `count`, but also increases it. Note that the modification is done "in place". The value of `count` is modified exactly in the environment where it was found.
|
||||
|
||||

|
||||
|
||||
|
@ -437,7 +437,7 @@ After the loop, `i` is not visible.
|
|||
|
||||
We also can use a "bare" code block `{…}` to isolate variables into a "local scope".
|
||||
|
||||
For instance, in a web browser all scripts share the same global area. So if we create a global variable in one script, it becomes available to others. But that become a source of conflicts if two scripts use the same variable name and overwrite each other.
|
||||
For instance, in a web browser all scripts share the same global area. So if we create a global variable in one script, it becomes available to others. But that becomes a source of conflicts if two scripts use the same variable name and overwrite each other.
|
||||
|
||||
That may happen if the variable name is a widespread word, and script authors are unaware of each other.
|
||||
|
||||
|
@ -590,7 +590,7 @@ Lexical Environment objects that we've been talking about are subjects to same m
|
|||
|
||||
### Real-life optimizations
|
||||
|
||||
As we've seen, in theory while a function is alive, all outer variabels are also retained.
|
||||
As we've seen, in theory while a function is alive, all outer variables are also retained.
|
||||
|
||||
But in practice, JavaScript engines try to optimize that. They analyze variable usage and if it's easy to see that an outer variable is not used -- it is removed.
|
||||
|
||||
|
@ -639,6 +639,6 @@ g();
|
|||
```warn header="See ya!"
|
||||
This feature of V8 is good to know. If you are debugging with Chrome/Opera, sooner or later you will meet it.
|
||||
|
||||
That is not a bug of debugger, but a special feature of V8. Maybe it will be changed sometimes.
|
||||
That is not a bug of debugger, but a special feature of V8. Maybe it will be changed some time.
|
||||
You always can check for it by running examples on this page.
|
||||
```
|
||||
|
|
|
@ -49,7 +49,7 @@ alert(test); // true, the variable lives after if
|
|||
|
||||
If we used `let test` on the 2nd line, then it wouldn't be visible to `alert`. But `var` ignores code blocks, so we've got a global `test`.
|
||||
|
||||
The same thing for loops: `var` can not be block or loop-local:
|
||||
The same thing for loops: `var` cannot be block- or loop-local:
|
||||
|
||||
```js
|
||||
for(var i = 0; i < 10; i++) {
|
||||
|
@ -130,7 +130,7 @@ function sayHi() {
|
|||
|
||||
People also call such behavior "hoisting" (raising), because all `var` are "hoisted" (raised) to the top of the function.
|
||||
|
||||
So in the example above, `if (false)` branch never executes, but that doesn't matter. The `var` inside it is processed in the beginning of the function, so at the moment of `(*)` there variable exists.
|
||||
So in the example above, `if (false)` branch never executes, but that doesn't matter. The `var` inside it is processed in the beginning of the function, so at the moment of `(*)` the variable exists.
|
||||
|
||||
**Declarations are hoisted, but assignments are not.**
|
||||
|
||||
|
@ -153,7 +153,7 @@ The line `var phrase = "Hello"` has two actions in it:
|
|||
1. Variable declaration `var`
|
||||
2. Variable assignment `=`.
|
||||
|
||||
The declaration is processed at the start of function execution ("hoisted"), but the assignment always works at the place where it is. So the code works essentially like this:
|
||||
The declaration is processed at the start of function execution ("hoisted"), but the assignment always works at the place where it appears. So the code works essentially like this:
|
||||
|
||||
```js run
|
||||
function sayHi() {
|
||||
|
|
|
@ -24,7 +24,7 @@ It does two things:
|
|||
|
||||
The same applies to other built-ins. E.g. we can use `window.Array` instead of `Array`.
|
||||
|
||||
2. Provides access to global Function Declarations and `var` variables. We can read them and write using its properties, for instance:
|
||||
2. Provides access to global Function Declarations and `var` variables. We can read and write them using its properties, for instance:
|
||||
|
||||
<!-- no-strict to move variables out of eval -->
|
||||
```js untrusted run no-strict refresh
|
||||
|
@ -38,7 +38,7 @@ It does two things:
|
|||
alert( window.phrase ); // Hello (global var)
|
||||
alert( window.sayHi ); // function (global function declaration)
|
||||
|
||||
// can write to window (creates a new sglobal variable)
|
||||
// can write to window (creates a new global variable)
|
||||
window.test = 5;
|
||||
|
||||
alert(test); // 5
|
||||
|
@ -57,7 +57,7 @@ alert("user" in window); // false
|
|||
```smart header="The global object is not a global Environment Record"
|
||||
In versions of ECMAScript prior to ES-2015, there were no `let/const` variables, only `var`. And global object was used as a global Environment Record (wordings were a bit different, but that's the gist).
|
||||
|
||||
But starting from ES-2015, these entities are split apart. There's a global Lexical Environment with its Environment Record. And there's a global object that provides *some* of global variables.
|
||||
But starting from ES-2015, these entities are split apart. There's a global Lexical Environment with its Environment Record. And there's a global object that provides *some* of the global variables.
|
||||
|
||||
As a practical difference, global `let/const` variables are definitively properties of the global Environment Record, but they do not exist in the global object.
|
||||
|
||||
|
@ -121,7 +121,7 @@ Usually, it's not a good idea to use it, but here are some examples you can meet
|
|||
|
||||
A browser may open multiple windows and tabs. A window may also embed another one in `<iframe>`. Every browser window has its own `window` object and global variables. JavaScript allows windows that come from the same site (same protocol, host, port) to access variables from each other.
|
||||
|
||||
That use is a little bit beyound our scope for now, but it looks like:
|
||||
That use is a little bit beyond our scope for now, but it looks like:
|
||||
```html run
|
||||
<iframe src="/" id="iframe"></iframe>
|
||||
|
||||
|
@ -158,7 +158,7 @@ Sometimes, the value of `this` is exactly the global object. That's rarely used,
|
|||
|
||||
Other, non-browser environments, may use another value for `this` in such cases.
|
||||
|
||||
2. When a function with `this` is called in not-strict mode, it gets the global object as `this`:
|
||||
2. When a function with `this` is called in non-strict mode, it gets the global object as `this`:
|
||||
```js run no-strict
|
||||
// not in strict mode (!)
|
||||
function f() {
|
||||
|
|
|
@ -44,7 +44,7 @@ function f(sayHi = function() {}) {
|
|||
f();
|
||||
```
|
||||
|
||||
In the specification, this feature is called a "contextual name". If the function does not provide one, then in an assignment is figured out from the context.
|
||||
In the specification, this feature is called a "contextual name". If the function does not provide one, then in an assignment it is figured out from the context.
|
||||
|
||||
Object methods have names too:
|
||||
|
||||
|
@ -104,7 +104,7 @@ When a user answers, it calls the handlers. We can pass two kinds of handlers:
|
|||
- A zero-argument function, then it is only called for a positive answer.
|
||||
- A function with arguments, then it is called in any case and gets the answer.
|
||||
|
||||
The idea is that we have a simple no-arguments handler syntax for positive cases (most often variant), but allow to provide universal handlers as well.
|
||||
The idea is that we have a simple no-arguments handler syntax for positive cases (most frequent variant), but allow to provide universal handlers as well.
|
||||
|
||||
To call `handlers` the right way, we examine the `length` property:
|
||||
|
||||
|
@ -155,7 +155,7 @@ alert( `Called ${sayHi.counter} times` ); // Called 2 times
|
|||
```warn header="A property is not a variable"
|
||||
A property assigned to a function like `sayHi.counter = 0` does *not* define a local variable `counter` inside it. In other words, a property `counter` and a variable `let counter` are two unrelated things.
|
||||
|
||||
We can treat a function as an object, store properties in it, but that has no effect on its execution. Variables never use function properties and vise versa. These are just parallel words.
|
||||
We can treat a function as an object, store properties in it, but that has no effect on its execution. Variables never use function properties and vice versa. These are just parallel words.
|
||||
```
|
||||
|
||||
Function properties can replace the closure sometimes. For instance, we can rewrite the counter example from the chapter <info:closure> to use a function property:
|
||||
|
|
|
@ -4,7 +4,7 @@ importance: 5
|
|||
|
||||
# Output every second
|
||||
|
||||
Write a function `printNumbers(from, to)` that outputs a number every second, starting from `from` and ending with `two`.
|
||||
Write a function `printNumbers(from, to)` that outputs a number every second, starting from `from` and ending with `to`.
|
||||
|
||||
Make two variants of the solution.
|
||||
|
||||
|
|
|
@ -158,7 +158,7 @@ The `setTimeout` above schedules next call right at the end of the current one `
|
|||
|
||||
Recursive `setTimeout` is more flexible method than `setInterval`. This way the next call may be scheduled differently, depending on the results of the current one.
|
||||
|
||||
For instance, we need to write a service that each 5 seconds sends a request to server asking for data, but in case if the server is overloaded, it should increase the interval to 10, 20, 60 seconds...
|
||||
For instance, we need to write a service that each 5 seconds sends a request to server asking for data, but in case the server is overloaded, it should increase the interval to 10, 20, 40 seconds...
|
||||
|
||||
Here's the pseudocode:
|
||||
```js
|
||||
|
@ -226,7 +226,7 @@ And here is the picture for recursive `setTimeout`:
|
|||
That's because a new call is planned at the end of the previous one.
|
||||
|
||||
````smart header="Garbage collection"
|
||||
When a function is passed in `setInterval/setTimeout`, an internal reference is created to it and saved in the scheduler. It prevents the function form being garbage collected, even if there are no other references to it.
|
||||
When a function is passed in `setInterval/setTimeout`, an internal reference is created to it and saved in the scheduler. It prevents the function from being garbage collected, even if there are no other references to it.
|
||||
|
||||
```js
|
||||
// the function stays in memory until the scheduler calls it
|
||||
|
@ -391,7 +391,7 @@ For server-side JavaScript, that limitation does not exist, and there exist othe
|
|||
|
||||
Another benefit for in-browser scripts is that they can show a progress bar or something to the user. That's because the browser usually does all "repainting" after the script is complete.
|
||||
|
||||
So if we do a single huge function then even it changes something, the changes are not reflected in the document till it finishes.
|
||||
So if we do a single huge function then even if it changes something, the changes are not reflected in the document till it finishes.
|
||||
|
||||
Here's the demo:
|
||||
```html run
|
||||
|
|
|
@ -6,7 +6,7 @@ importance: 5
|
|||
|
||||
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.
|
||||
|
||||
**The differnce from `debounce` -- if an ignored call is the last during the cooldown, then it executes at the end of the delay.**
|
||||
**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.
|
||||
|
||||
|
@ -45,4 +45,4 @@ f1000(3); // (throttling, 1000ms not out yet)
|
|||
// ...outputs 3, intermediate value 2 was ignored
|
||||
```
|
||||
|
||||
P.S. Arguments and the context `this` passed to `f1000` should be passed to the original `f`.
|
||||
P.S. Arguments and the context `this` passed to `f1000` should be passed to the original `f`.
|
||||
|
|
|
@ -8,7 +8,7 @@ JavaScript gives exceptional flexibility when dealing with functions. They can b
|
|||
|
||||
Let's say we have a function `slow(x)` which is CPU-heavy, but its results are stable. In other words, for the same `x` it always returns the same result.
|
||||
|
||||
If the function is called often, we may want to cache (remember) the results for different `x` to evade spending extra-time on recalculations.
|
||||
If the function is called often, we may want to cache (remember) the results for different `x` to avoid spending extra-time on recalculations.
|
||||
|
||||
But instead of adding that functionality into `slow()` we'll create a wrapper. As we'll see, there are many benefits of doing so.
|
||||
|
||||
|
@ -110,11 +110,11 @@ alert( worker.slow(2) ); // Whoops! Error: Cannot read property 'someMethod' of
|
|||
*/!*
|
||||
```
|
||||
|
||||
The error occurs in the line `(*)` that tries to access `this.someMethod` and fails. Guess you can see, why?
|
||||
The error occurs in the line `(*)` that tries to access `this.someMethod` and fails. Can you see why?
|
||||
|
||||
The reason is that the wrapper calls the original function as `func(x)` in the line `(**)`. And, when called like this, the function gets `this = undefined`.
|
||||
The reason is that the wrapper calls the original function as `func(x)` in the line `(**)`. And, when called like that, the function gets `this = undefined`.
|
||||
|
||||
As if we run:
|
||||
We would observe a similar symptom if we tried to run:
|
||||
|
||||
```js
|
||||
let func = worker.slow;
|
||||
|
@ -133,7 +133,7 @@ The syntax is:
|
|||
func.call(context, arg1, arg2, ...)
|
||||
```
|
||||
|
||||
It runs the `func` providing the first argument as `this`, and the next as the arguments.
|
||||
It runs `func` providing the first argument as `this`, and the next as the arguments.
|
||||
|
||||
To put it simple, these two calls do almost the same:
|
||||
```js
|
||||
|
@ -173,7 +173,7 @@ say.call( user, "Hello" ); // John: Hello
|
|||
```
|
||||
|
||||
|
||||
For our case, we can use `call` in the wrapper to pass the context to the original function:
|
||||
In our case, we can use `call` in the wrapper to pass the context to the original function:
|
||||
|
||||
|
||||
```js run
|
||||
|
@ -210,7 +210,7 @@ alert( worker.slow(2) ); // works, doesn't call the original (cached)
|
|||
|
||||
Now everything is fine.
|
||||
|
||||
To put all clear, let's see more deeply how `this` is passed along:
|
||||
To make it all clear, let's see more deeply how `this` is passed along:
|
||||
|
||||
1. After the decoration `worker.slow` is now the wrapper `function (x) { ... }`.
|
||||
2. So when `worker.slow(2)` is executed, the wrapper gets `2` as an argument and `this=worker` (it's the object before dot).
|
||||
|
@ -305,11 +305,11 @@ If we look more closely, there's a minor difference between such uses of `call`
|
|||
- The spread operator `...` allows to pass *iterable* `args` as the list to `call`.
|
||||
- The `apply` accepts only *array-like* `args`.
|
||||
|
||||
So, these calls complement to each other. Where we expect an iterable, `call` works, where we expect an array-like, `apply` works.
|
||||
So, these calls complement each other. Where we expect an iterable, `call` works, where we expect an array-like, `apply` works.
|
||||
|
||||
And if `args` is both iterable and array-like, like a real array, then we technically could use any of them, but `apply` will probably be faster, because it's a single operation. Most JavaScript engines internally optimize is better than a pair `call + spread`.
|
||||
|
||||
One of most important uses of `apply` is passing the call to another function, like this:
|
||||
One of the most important uses of `apply` is passing the call to another function, like this:
|
||||
|
||||
```js
|
||||
let wrapper = function() {
|
||||
|
@ -438,7 +438,7 @@ So, technically it takes `this` and joins `this[0]`, `this[1]` ...etc together.
|
|||
|
||||
*Decorator* is a wrapper around a function that alters its behavior. The main job is still carried out by the function.
|
||||
|
||||
It is generally safe to replace a function or a method with decorated one, except for one little thing. If the original function had properties on it, like `func.calledCount` or whatever, then the decorated one will not provide them. Because that is a wrapper. So one need to be careful if he uses them. Some decorators provide their own properties.
|
||||
It is generally safe to replace a function or a method with a decorated one, except for one little thing. If the original function had properties on it, like `func.calledCount` or whatever, then the decorated one will not provide them. Because that is a wrapper. So one need to be careful if one uses them. Some decorators provide their own properties.
|
||||
|
||||
Decorators can be seen as "features" or "aspects" that can be added to a function. We can add one or add many. And all this without changing its code!
|
||||
|
||||
|
@ -458,4 +458,4 @@ let wrapper = function() {
|
|||
We also saw an example of *method borrowing* when we take a method from an object and `call` it in the context of another object. It is quite common to take array methods and apply them to arguments. The alternative is to use rest parameters object that is a real array.
|
||||
|
||||
|
||||
There are many decorators there in the wilds. Check how well you got them by solving the tasks of this chapter.
|
||||
There are many decorators there in the wild. Check how well you got them by solving the tasks of this chapter.
|
||||
|
|
|
@ -10,6 +10,6 @@ f = f.bind( {name: "John"} ).bind( {name: "Pete"} );
|
|||
f(); // John
|
||||
```
|
||||
|
||||
The exotic [bound function](https://tc39.github.io/ecma262/#sec-bound-function-exotic-objects) object returned by `f.bind(...)` remembers the context (and arguments if provided) only at the creation time.
|
||||
The exotic [bound function](https://tc39.github.io/ecma262/#sec-bound-function-exotic-objects) object returned by `f.bind(...)` remembers the context (and arguments if provided) only at creation time.
|
||||
|
||||
A function cannot be re-bound.
|
||||
|
|
|
@ -2,7 +2,7 @@ importance: 5
|
|||
|
||||
---
|
||||
|
||||
# Ask loosing this
|
||||
# Ask losing this
|
||||
|
||||
The call to `askPassword()` in the code below should check the password and then call `user.loginOk/loginFail` depending on the answer.
|
||||
|
||||
|
|
|
@ -5,15 +5,15 @@ libs:
|
|||
|
||||
# Function binding
|
||||
|
||||
When using `setTimeout` with object methods or passing object methods along, there's a known problem: "loosing `this`".
|
||||
When using `setTimeout` with object methods or passing object methods along, there's a known problem: "losing `this`".
|
||||
|
||||
Suddenly, `this` just stops working right. The situation is typical for novice developers, but happens with experienced ones as well.
|
||||
|
||||
[cut]
|
||||
|
||||
## Loosing "this"
|
||||
## Losing "this"
|
||||
|
||||
We already know that in JavaScript it's easy to loose `this`. Once a method is passed somewhere separately from the object -- `this` is lost.
|
||||
We already know that in JavaScript it's easy to lose `this`. Once a method is passed somewhere separately from the object -- `this` is lost.
|
||||
|
||||
Here's how it may happen with `setTimeout`:
|
||||
|
||||
|
@ -72,7 +72,7 @@ setTimeout(() => user.sayHi(), 1000); // Hello, John!
|
|||
|
||||
Looks fine, but a slight vulnerability appears in our code structure.
|
||||
|
||||
What is before `setTimeout` triggers (there's one second delay!) `user` changes value? Then, suddenly, the it will call the wrong object!
|
||||
What if before `setTimeout` triggers (there's one second delay!) `user` changes value? Then, suddenly, the it will call the wrong object!
|
||||
|
||||
|
||||
```js run
|
||||
|
|
|
@ -105,7 +105,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 the spread operator, 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.
|
||||
|
||||
|
@ -160,7 +160,7 @@ function curry(f) {
|
|||
|
||||
Advanced currying allows both to keep the function callable normally and to get partials easily. To understand the benefits we definitely need a worthy real-life example.
|
||||
|
||||
For instance, we have the logging function `log(date, importance, message)` that formats and output the information. In real projects such functions also have many other useful features like: sending it over the network or filtering:
|
||||
For instance, we have the logging function `log(date, importance, message)` that formats and outputs the information. In real projects such functions also have many other useful features like: sending it over the network or filtering:
|
||||
|
||||
```js
|
||||
function log(date, importance, message) {
|
||||
|
@ -205,7 +205,7 @@ todayDebug("message"); // [HH:mm] DEBUG message
|
|||
```
|
||||
|
||||
So:
|
||||
1. We didn't loose anything after currying: `log` is still callable normally.
|
||||
1. We didn't lose anything after currying: `log` is still callable normally.
|
||||
2. We were able to generate partial functions that are convenient in many cases.
|
||||
|
||||
## Advanced curry implementation
|
||||
|
|
|
@ -125,4 +125,4 @@ Arrow functions:
|
|||
- Can't be called with `new`.
|
||||
- (They also don't have `super`, but we didn't study it. Will be in the chapter <info:class-inheritance>).
|
||||
|
||||
That's because they are meant for short pieces of code that does not have the own "context", but rather works in the current one. And they really shine in that use case.
|
||||
That's because they are meant for short pieces of code that does not have their own "context", but rather works in the current one. And they really shine in that use case.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue