Skip to content

Commit

Permalink
up
Browse files Browse the repository at this point in the history
  • Loading branch information
iliakan committed Nov 22, 2016
1 parent 63f55dc commit 83b93e5
Show file tree
Hide file tree
Showing 49 changed files with 689 additions and 271 deletions.
24 changes: 23 additions & 1 deletion 1-js/4-object-basics/04-object-methods/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,10 +219,11 @@ Please note that usually a call of a function using `this` without an object is
```smart header="The consequences of unbound `this`"
If you come from another programming languages, then you are probably used to an idea of a "bound `this`", where methods defined in an object always have `this` referencing that object.

The idea of unbound, run-time evaluated `this` has both pluses and minuses. From one side, a function can be reused for different objects. From the other side, greater flexibility opens a place for mistakes.
The idea of unbound, run-time evaluated `this` has both pluses and minuses. From one side, a function can be reused for different objects. From the other side, greater flexibility opens a place for mistakes.

Here we are not to judge whether this language design decision is good or bad. We will understand how to work with it, how to get benefits and evade problems.
```
## Internals: Reference Type
An intricate method call can loose `this`, for instance:
Expand Down Expand Up @@ -295,6 +296,27 @@ Any other operation like assignment `hi = user.hi` discards the reference type a

So, as the result, the value of `this` is only passed the right way if the function is called directly using a dot `obj.method()` or square brackets `obj[method]()` syntax (they do the same here).

````warn header="Arrow functions do not have `this`"
Arrow functions are special: they don't have "own" `this`. If we reference `this` from such function, it's taken from the outer "normal" function.

For instance, here `arrow()` uses `this` from the outer `user.sayHi()` method:

```js run
let user = {
firstName: "Ilya",
sayHi() {
let arrow = () => alert(this.firstName);
arrow();
}
};

user.sayHi(); // Ilya
```

That's a special feature of arrow functions, it's useful when we actually do not want to have a separate `this`, but rather to take it from the outer context. Later in the chapter <info:arrow-functions> we'll dig more deeply into what's going on.

````
## Summary
[todo]
Expand Down
33 changes: 33 additions & 0 deletions 1-js/5-data-types/01-primitives-methods/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,39 @@ alert( n.toFixed(2) ); // 1.23

We'll see more specific methods in chapters <info:number> and <info:string>.


````warn header="Constructors `String/Number/Boolean` are for internal use only"
Some languages like Java allow to create "wrapper objects" for primitives explicitly using syntax like `new Number(1)` or `new Boolean(false)`.

In Javascript that's also possible for historical reasons, but highly not recommended. Things will go crazy in many places.

For instance:

```js run
alert( typeof 1 ); // "number"

alert( typeof new Number(1) ); // "object"!
```

And, because `zero` is an object:

```js run
let zero = new Number(0);

if (zero) { // zero is true, because it's an object
alert( "zero is truthy?!?" );
}
```

From the other side, using same functions `String/Number/Boolean` without `new` is a totally sane and useful thing. They convert a value to the corresponding type: to a string, a number, or a boolean (primitive).

This is totally valid:
```js
let num = Number("123"); // convert a string to number
```
````
````warn header="null/undefined have no methods"
Special primitives `null` and `undefined` are exceptions. They have no corresponding "wrapper objects" and provide no methods. In a sense, they are "the most primitive".
Expand Down
25 changes: 12 additions & 13 deletions 1-js/5-data-types/05-array-methods/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -606,32 +606,31 @@ alert(typeof {}); // object
alert(typeof []); // same
```

There's a special method for that [Array.isArray(value)](mdn:js/Array/isArray) that returns `true` if the `value` is an array, and `false` otherwise.
...But arrays are used so often that there's a special method for that: [Array.isArray(value)](mdn:js/Array/isArray). It returns `true` if the `value` is an array, and `false` otherwise.

```js run
alert(Array.isArray({})); // false

alert(Array.isArray([])); // true
```

```smart header="`Array.isArray` vs other type-checks"
You remembeare other ways to check for

## Methods: "thisArg"

Almost all array methods that call functions -- like `find`, `filter`, `map`, with a notable exception of `sort`, accept an optional additional parameter `thisArg`.

The full syntax is:
In the sections above that parameter is not explained, because it's rarely used.

But for completeness here's the full syntax:

```js
let result = arr.find(func, thisArg);
let results = arr.filter(func, thisArg);
// etc, thisArg goes after the function
arr.find(func, thisArg);
arr.filter(func, thisArg);
arr.map(func, thisArg);
// ...
// thisArg is the optional last argument
```

It is used sparingly, but we have to cover it here for the sake of completeness.

The value of `thisArg` parameter becomes `this` for the function.
The value of `thisArg` parameter becomes `this` for `func`.

For instance, here we use an object method as a filter:

Expand All @@ -657,7 +656,7 @@ let youngerUsers = users.filter(user.younger, user);
alert(youngerUsers.length); // 2
```

In the call above, we use `user.younger` as a filter and also provide `user` as the context for it. If we did't provide the context, `users.filter(user.younger)` would call `user.younger` as a standalone function, with `this=undefined`. That would be an instant error.
In the call above, we use `user.younger` as a filter and also provide `user` as the context for it. If we did't provide the context, `users.filter(user.younger)` would call `user.younger` as a standalone function, with `this=undefined`. That would mean an instant error.

## Other methods

Expand All @@ -676,7 +675,7 @@ These and other methods are also listed in the [manual](mdn:js/Array).

## Summary

Most often methods:
Most used array methods:

- `split/join` -- convert a string to array and back.
- `splice` -- delete and insert elements at the given position.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,23 @@ Also, it always has all arguments in it, we can't capture them partially, like w

So when we need these features, then rest parameters are preferred.

````smart header="Arrow functions do not have `\"arguments\"`"
If we access the `arguments` object from an arrow function, it takes them from the outer "normal" function.

Here's an example:

```js run
function f() {
let showArg = () => alert(arguments[0]);
showArg();
}

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.

````
## Spread operator [#spread-operator]
We've just seen how to get an array from the list of parameters.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Both nested functions are created within the same Lexical Environment.
Surely it will work just fine.

So they share the same `count`:
Both nested functions are created within the same outer Lexical Environment, so they share access to the same `count` variable:

```js run
function Counter() {
Expand All @@ -10,6 +10,7 @@ function Counter() {
this.up = function() {
return ++count;
};

this.down = function() {
return --count;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1 @@
Решение аналогично задаче <info:task/logging-decorator>, разница в том, что в лог вместо одного аргумента идет весь объект `arguments`.

Для передачи вызова с произвольным количеством аргументов используем `f.apply(this, arguments)`.

```js run
function work(a, b) {
alert( a + b ); // work - произвольная функция
}

function makeLogging(f, log) {

*!*
function wrapper() {
log.push([].slice.call(arguments));
return f.apply(this, arguments);
}
*/!*

return wrapper;
}

var log = [];
work = makeLogging(work, log);

work(1, 2); // 3
work(4, 5); // 9

for (var i = 0; i < log.length; i++) {
var args = log[i]; // массив из аргументов i-го вызова
alert( 'Лог:' + args.join() ); // "Лог:1,2", "Лог:4,5"
}
```

Here we can use `log.push(args)` to store all arguments in the log and `f.apply(this, args)` to forward the call.
1 change: 0 additions & 1 deletion 1-js/8-more-functions/08-call-apply-decorators/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,6 @@ Taken from the specification almost "as-is":

So, technically it takes `this` and joins `this[0]`, `this[1]` ...etc together. It's intentionally written in a way that allows any array-like `this` (not a coincidence, many methods follow this practice). That's why it also works with `this=arguments`.


## Summary

*Decorator* is a wrapper around a function that alters its behavior. The main job is still carried out by the function.
Expand Down
4 changes: 2 additions & 2 deletions 1-js/8-more-functions/09-bind/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ let f = user.sayHi;
setTimeout(f, 1000); // lost user context
```

The method `setTimeout` is a little special: it sets `this=window` for the function call. So for `this.firstName` it tries to get `window.firstName`, which does not exist. In other similar cases as we'll see, usually `this` just becomes `undefined`.
The method `setTimeout` in-browser is a little special: it sets `this=window` for the function call (for Node.JS, `this` becomes the timer object, but doesn't really matter here). So for `this.firstName` it tries to get `window.firstName`, which does not exist. In other similar cases as we'll see, usually `this` just becomes `undefined`.

The task is quite typical -- we want to pass an object method somewhere else (here -- to the scheduler) where it will be called. How to make sure that it will be called in the right context?

Expand Down Expand Up @@ -494,4 +494,4 @@ But most implementations of currying in Javascript are advanced, as described: t
- *Currying* is a transform that makes `f(a,b,c)` callable as `f(a)(b)(c)`. Javascript implementations usually both keep the function callable normally and return the partial if arguments count is not enough.
Currying is convenient when we want to have easy partials. We saw an example with logging: the universal function `log(date, importance, message)` after currying gives us partials when called with one argument like `log(date)` or two arguments `log(date, importance)`.
Currying is great when we want easy partials. As we've seen in the logging example: the universal function `log(date, importance, message)` after currying gives us partials when called with one argument like `log(date)` or two arguments `log(date, importance)`.
130 changes: 130 additions & 0 deletions 1-js/8-more-functions/10-arrow-functions/article.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# Arrow functions revisited

Let's revisit arrow functions.

[cut]

Arrow functions are not just a "shorthand" for writing small stuff.

Javascript is full of situations where we need to write a small function, that's executed somewhere else.

For instance:

- `arr.forEach(func)` -- `func` is executed by `forEach` for every array item.
- `setTimeout(func)` -- `func` is executed by the built-in scheduler.
- ...there are more.

It's very in the spirit of Javascript to create a function and pass it somewhere.

And in such functions we usually don't want to leave the current context.

## Arrow functions have no "this"

As we remember from the chapter <info:object-methods>, arrow functions do not have `this`. If `this` is accessed, it is taken from the outside.

For instance, we can use it to iterate inside an object method:

```js run
let group = {
title: "Our Group",
students: ["John", "Pete", "Alice"],

showList() {
*!*
this.students.forEach(
student => alert(this.title + ': ' + student)
);
*/!*
}
};

group.showList();
```

Here in `forEach`, the arrow function is used, so `this.title` in it is exactly the same as in the outer method `showList`. That is: `group.title`.

If we used a "regular" function, there would be an error:

```js run
let group = {
title: "Our Group",
students: ["John", "Pete", "Alice"],

showList() {
*!*
this.students.forEach(function(student) {
// Error: Cannot read property 'title' of undefined
alert(this.title + ': ' + student)
});
*/!*
}
};

group.showList();
```

The error occurs because `forEach` runs functions with `this=undefined` by default, so the attempt to access `undefined.title` is made.

That doesn't affect arrow functions, because they just don't have `this`.

```warn header="Arrow functions can't run with `new`"
Not having `this` naturally means another limitation: arrow functions can't be used as constructors. They can't be called with `new`.
```
```smart header="Arrow functions VS bind"
There's a subtle difference between an arrow function `=>` and a regular function called with `.bind(this)`:
- `.bind(this)` creates a "bound version" of the function.
- The arrow `=>` doesn't create any binding. The function simply doesn't have `this`. The lookup of `this` is made exactly the same way as a regular variable search: in the outer lexical environment.
```

## Arrows have no "arguments"

Arrow functions also have no `arguments` variable.

That's great for decorators, when we need to forward a call with the current `this` and `arguments`.

For instance, `defer(f, ms)` gets a function and returns a wrapper around it that delays the call by `ms` milliseconds:

```js run
function defer(f, ms) {
return function() {
setTimeout(() => f.apply(this, arguments), ms)
};
}

function sayHi(who) {
alert('Hello, ' + who);
}

let sayHiDeferred = defer(sayHi, 2000);
sayHiDeferred("John"); // Hello, John after 2 seconds
```

The same without an arrow function would look like:

Аналогичная реализация без функции-стрелки выглядела бы так:

```js
function defer(f, ms) {
return function(...args) {
let ctx = this;
setTimeout(function() {
return f.apply(ctx, args);
}, ms);
};
}
```

Here we had to create additional variables `args` and `ctx` so that the function inside `setTimeout` could take them.

## Summary

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. 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.
8 changes: 3 additions & 5 deletions 1-js/9-object-inheritance/04-function-prototype/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,15 +108,13 @@ That's handy when we have an object, don't know which constructor was used for i

...But probably the most important thing about `"constructor"` is that...

**JavaScript itself does not use the `"constructor"` property at all.**
**JavaScript itself does not ensure the right `"constructor"` at all.**

Yes, it exists in the default `"prototype"` for functions, but that's literally all about it. No language function relies on it and nothing controls its validity.

It is created automatically, but what happens with it later -- is totally on us.
Yes, it exists in the default `"prototype"` for functions, but that's all. It is created automatically, but what happens with it later -- is totally on us.

In particular, if we replace the default prototype by assigning our own `Rabbit.prototype = { jumps: true }`, then there will be no `"constructor"` in it.

Such assignment won't break native methods or syntax, because nothing in the language uses the `"constructor"` property. But we may want to keep `"constructor"` for convenience by adding properties to the default `"prototype"` instead of overwriting it as a whole:
But we may want to keep `"constructor"` for convenience by adding properties to the default `"prototype"` instead of overwriting it as a whole:

```js
function Rabbit() {}
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 83b93e5

Please sign in to comment.