diff --git a/1-js/01-getting-started/1-intro/article.md b/1-js/01-getting-started/1-intro/article.md index 7d13a8857..2f88ab59d 100644 --- a/1-js/01-getting-started/1-intro/article.md +++ b/1-js/01-getting-started/1-intro/article.md @@ -26,7 +26,7 @@ Interpretadores diferentes têm "codinomes" diferentes. Por exemplo: - [V8](https://en.wikipedia.org/wiki/V8_(JavaScript_engine)) -- no Chrome e no Opera. - [SpiderMonkey](https://en.wikipedia.org/wiki/SpiderMonkey) -- no Firefox. -- ...Há outros codinomes como "Chakra" para o IE, "ChakraCore" para Microsoft Edge, "Nitro" e "SquirrelFish" para Safari, etc. +- ...Há outros codinomes como "Chakra" para o IE, "JavaScriptCore", "Nitro" e "SquirrelFish" para Safari, etc. Os termos acima são bons para lembrar, pois são usados em artigos de desenvolvedores na internet. Vamos usá-los também. Por exemplo, se "um recurso X é suportado pelo V8", então ele provavelmente funciona no Chrome e no Opera. diff --git a/1-js/02-first-steps/02-structure/article.md b/1-js/02-first-steps/02-structure/article.md index 3b80aea66..1b998aa68 100644 --- a/1-js/02-first-steps/02-structure/article.md +++ b/1-js/02-first-steps/02-structure/article.md @@ -46,7 +46,7 @@ alert(3 + + 2); ``` -O código produz `6` porque o Javascript não insere pontos e virgulas aqui. É intuitivamente óbvio que se a linha termina com um sinal de mais `"+"`, então é uma "expressão incompleta", logo o ponto e vírgula não é necessário. E neste caso isso funciona como pretendido. +O código produz `6` porque o Javascript não insere pontos e virgulas aqui. É intuitivamente óbvio que se a linha termina com um sinal de mais `"+"`, então é uma "expressão incompleta", logo o ponto e vírgula aí seria incorreto. E neste caso isso funciona como pretendido. **Mas há situações em que o JavaScript "falha" em assumir um ponto e vírgula onde ele é realmente necessário.** @@ -56,40 +56,36 @@ Erros que ocorrem em tais casos são bastante difíceis de encontrar e corrigir. Se você está curioso para ver um exemplo concreto de tal erro, verifique este código: ```js run -[1, 2].forEach(alert) +alert("Hello"); + +[1, 2].forEach(alert); ``` -Não há necessidade de pensar sobre o significado dos parênteses `[]` e `forEach` ainda. Nós vamos estudá-los mais tarde. Por enquanto, apenas lembre-se que o resultado do código: mostra `1` e depois` 2`. +Não há necessidade de pensar sobre o significado dos parênteses `[]` e também do `forEach`. Nós vamos estudá-los mais tarde. Por enquanto, apenas lembre-se do resultado da execução do código: ele mostra `Hello`, depois `1`, e depois` 2`. -Agora, vamos adicionar um `alert` antes do código e * não * terminá-lo com um ponto e vírgula: +Agora, vamos remover o ponto e vírgula depois do `alert`: ```js run no-beautify -alert("Haverá um erro") +alert("Hello") -[1, 2].forEach(alert) +[1, 2].forEach(alert); ``` -Agora, se nós executarmos o código, apenas o primeiro `alert` é mostrado e então temos um erro! - -Mas tudo está bem novamente se adicionarmos um ponto e vírgula após `alert`: -```js run -alert("Tudo bem agora"); +A diferença em comparação com o código acima é de apenas um caractere: o ponto e vírgula da primeira linha se foi. -[1, 2].forEach(alert) -``` +Se nós executarmos esse código, apenas o primeiro `Hello` é mostrado (e então há um erro, você pode precisar de abrir a consola para o ver). Já não existem mais números. -Agora temos a mensagem "Tudo bem agora" seguida por "1" e "2". +Isso ocorre porque o JavaScript não assume um ponto e vírgula antes dos colchetes `[...]`. Portanto, o código no último exemplo é tratado como uma única instrução. - -O erro na variante sem ponto e vírgula ocorre porque o JavaScript não assume um ponto e vírgula antes dos colchetes `[...]`. - -Portanto, como o ponto e vírgula não é inserido automaticamente, o código no primeiro exemplo é tratado como uma única instrução. Veja como o mecanismo vê isso: +Veja como o mecanismo vê isso: ```js run no-beautify -alert("Haverá um erro")[1, 2].forEach(alert) +alert("Hello")[1, 2].forEach(alert); ``` -Mas devem ser duas declarações separadas, não uma. Tal fusão neste caso é completamente errado, daí o erro. Isso pode acontecer em outras situações. +Parece estranho, não? Tal fusão neste caso é completamente errada. Nós precisamos de colocar um ponto e vírgula depois de `alert` para o código funcionar corretamente. + +Isso também pode acontecer em outras situações. ```` Recomendamos colocar ponto e vírgula entre as frases, mesmo que estejam separadas por novas linhas. Esta regra é amplamente adotada pela comunidade. Vamos notar mais uma vez -- *é possível* deixar de fora os pontos e vírgulas na maior parte do tempo. Mas é mais seguro -- especialmente para um iniciante -- usá-los. diff --git a/1-js/02-first-steps/05-types/article.md b/1-js/02-first-steps/05-types/article.md index a75367f1e..12f30f666 100644 --- a/1-js/02-first-steps/05-types/article.md +++ b/1-js/02-first-steps/05-types/article.md @@ -64,7 +64,7 @@ Os valores numéricos especiais pertencem formalmente ao tipo "número". Claro q Veremos mais sobre como trabalhar com números no capítulo . -## BigInt +## BigInt [#bigint-type] In JavaScript, the "number" type cannot represent integer values larger than (253-1) (that's `9007199254740991`), or less than -(253-1) for negatives. It's a technical limitation caused by their internal representation. diff --git a/1-js/02-first-steps/08-operators/article.md b/1-js/02-first-steps/08-operators/article.md index 498870e42..decf5bcfc 100644 --- a/1-js/02-first-steps/08-operators/article.md +++ b/1-js/02-first-steps/08-operators/article.md @@ -63,8 +63,8 @@ In school maths, we write that as ab. For instance: ```js run -alert( 2 ** 2 ); // 2² = 4 -alert( 2 ** 3 ); // 2³ = 8 +alert( 2 ** 2 ); // 2² = 4 +alert( 2 ** 3 ); // 2³ = 8 alert( 2 ** 4 ); // 2⁴ = 16 ``` diff --git a/1-js/02-first-steps/15-function-basics/article.md b/1-js/02-first-steps/15-function-basics/article.md index 843cb43bf..d02bc4d5a 100644 --- a/1-js/02-first-steps/15-function-basics/article.md +++ b/1-js/02-first-steps/15-function-basics/article.md @@ -20,10 +20,10 @@ function showMessage() { } ``` -A palavra-chave `function` vem primeiro, depois vem o *nome da função*, e uma lista de *parâmetros* entre os parêntesis (vazio no exemplo acima) e finalmente o código da função, também chamado de "o corpo da função", entre chaves. +A palavra-chave `function` vem primeiro, depois vem o *nome da função*, e uma lista de *parâmetros* entre os parêntesis (separados por vírgulas, vazia no exemplo acima, veremos exemplos mais tarde) e finalmente o código da função, também chamado de "o corpo da função", entre chaves. ```js -function name(parameters) { +function name(parameter1, parameter2, ... parameterN) { ...corpo... } ``` @@ -137,26 +137,23 @@ It's a good practice to minimize the use of global variables. Modern code has fe ## Parameters -We can pass arbitrary data to functions using parameters (also called *function arguments*) . +We can pass arbitrary data to functions using parameters. In the example below, the function has two parameters: `from` and `text`. ```js run -function showMessage(*!*from, text*/!*) { // arguments: from, text +function showMessage(*!*from, text*/!*) { // parameters: from, text alert(from + ': ' + text); } -*!* -showMessage('Ann', 'Hello!'); // Ann: Hello! (*) -showMessage('Ann', "What's up?"); // Ann: What's up? (**) -*/!* +*!*showMessage('Ann', 'Hello!');*/!* // Ann: Hello! (*) +*!*showMessage('Ann', "What's up?");*/!* // Ann: What's up? (**) ``` When the function is called in lines `(*)` and `(**)`, the given values are copied to local variables `from` and `text`. Then the function uses them. Here's one more example: we have a variable `from` and pass it to the function. Please note: the function changes `from`, but the change is not seen outside, because a function always gets a copy of the value: - ```js run function showMessage(from, text) { @@ -175,9 +172,21 @@ showMessage(from, "Hello"); // *Ann*: Hello alert( from ); // Ann ``` +When a value is passed as a function parameter, it's also called an *argument*. + +In other words, to put these terms straight: + +- A parameter is the variable listed inside the parentheses in the function declaration (it's a declaration time term) +- An argument is the value that is passed to the function when it is called (it's a call time term). + +We declare functions listing their parameters, then call them passing arguments. + +In the example above, one might say: "the function `sayMessage` is declared with two parameters, then called with two arguments: `from` and `"Hello"`". + + ## Default values -If a parameter is not provided, then its value becomes `undefined`. +If a function is called, but an argument is not provided, then the corresponding value becomes `undefined`. For instance, the aforementioned function `showMessage(from, text)` can be called with a single argument: @@ -185,9 +194,9 @@ For instance, the aforementioned function `showMessage(from, text)` can be calle showMessage("Ann"); ``` -That's not an error. Such a call would output `"Ann: undefined"`. There's no `text`, so it's assumed that `text === undefined`. +That's not an error. Such a call would output `"*Ann*: undefined"`. As the value for `text` isn't passed, it becomes `undefined`. -If we want to use a "default" `text` in this case, then we can specify it after `=`: +We can specify the so-called "default" (to use if omitted) value for a parameter in the function declaration, using `=`: ```js run function showMessage(from, *!*text = "no text given"*/!*) { @@ -211,19 +220,23 @@ function showMessage(from, text = anotherFunction()) { ```smart header="Evaluation of default parameters" In JavaScript, a default parameter is evaluated every time the function is called without the respective parameter. -In the example above, `anotherFunction()` is called every time `showMessage()` is called without the `text` parameter. +In the example above, `anotherFunction()` isn't called at all, if the `text` parameter is provided. + +On the other hand, it's independently called every time when `text` is missing. ``` ### Alternative default parameters -Sometimes it makes sense to set default values for parameters not in the function declaration, but at a later stage, during its execution. +Sometimes it makes sense to assign default values for parameters not in the function declaration, but at a later stage. -To check for an omitted parameter, we can compare it with `undefined`: +We can check if the parameter is passed during the function execution, by comparing it with `undefined`: ```js run function showMessage(text) { + // ... + *!* - if (text === undefined) { + if (text === undefined) { // if the parameter is missing text = 'empty message'; } */!* @@ -234,21 +247,21 @@ function showMessage(text) { showMessage(); // empty message ``` -...Or we could use the `||` operator: +...Or we could use the `??` operator: ```js -// if text parameter is omitted or "" is passed, set it to 'empty' function showMessage(text) { + // if text is undefined or otherwise falsy, set it to 'empty' text = text || 'empty'; ... } ``` -Modern JavaScript engines support the [nullish coalescing operator](info:nullish-coalescing-operator) `??`, it's better when falsy values, such as `0`, are considered regular: +Modern JavaScript engines support the [nullish coalescing operator](info:nullish-coalescing-operator) `??`, it's better when most falsy values, such as `0`, should be considered "normal": ```js run -// if there's no "count" parameter, show "unknown" function showCount(count) { + // if count is undefined or null, show "unknown" alert(count ?? "unknown"); } @@ -411,7 +424,7 @@ Functions that are used *very often* sometimes have ultrashort names. For example, the [jQuery](http://jquery.com) framework defines a function with `$`. The [Lodash](http://lodash.com/) library has its core function named `_`. -These are exceptions. Generally functions names should be concise and descriptive. +These are exceptions. Generally function names should be concise and descriptive. ``` ## Functions == Comments diff --git a/1-js/03-code-quality/01-debugging-chrome/article.md b/1-js/03-code-quality/01-debugging-chrome/article.md index e20dfd90b..fb793f218 100644 --- a/1-js/03-code-quality/01-debugging-chrome/article.md +++ b/1-js/03-code-quality/01-debugging-chrome/article.md @@ -1,4 +1,4 @@ -# Depuração de erros no Chrome +# Depuração de erros no navegador Antes de escrevermos código mais complexo, vamos falar de debugging (depuração de erros). diff --git a/1-js/03-code-quality/06-polyfills/article.md b/1-js/03-code-quality/06-polyfills/article.md index af38faad2..7efaf8677 100644 --- a/1-js/03-code-quality/06-polyfills/article.md +++ b/1-js/03-code-quality/06-polyfills/article.md @@ -48,7 +48,7 @@ Modern project build systems, such as [webpack](http://webpack.github.io/), prov New language features may include not only syntax constructs and operators, but also built-in functions. -For example, `Math.trunc(n)` is a function that "cuts off" the decimal part of a number, e.g `Math.trunc(1.23) = 1`. +For example, `Math.trunc(n)` is a function that "cuts off" the decimal part of a number, e.g `Math.trunc(1.23)` returns `1`. In some (very outdated) JavaScript engines, there's no `Math.trunc`, so such code will fail. diff --git a/1-js/04-object-basics/04-object-methods/article.md b/1-js/04-object-basics/04-object-methods/article.md index 0d3c1f392..a36b9ca07 100644 --- a/1-js/04-object-basics/04-object-methods/article.md +++ b/1-js/04-object-basics/04-object-methods/article.md @@ -81,7 +81,7 @@ user = { // method shorthand looks better, right? user = { *!* - sayHi() { // same as "sayHi: function()" + sayHi() { // same as "sayHi: function(){...}" */!* alert("Hello"); } diff --git a/1-js/04-object-basics/06-constructor-new/article.md b/1-js/04-object-basics/06-constructor-new/article.md index 5f8709ae1..184b30f86 100644 --- a/1-js/04-object-basics/06-constructor-new/article.md +++ b/1-js/04-object-basics/06-constructor-new/article.md @@ -64,13 +64,14 @@ Now if we want to create other users, we can call `new User("Ann")`, `new User(" That's the main purpose of constructors -- to implement reusable object creation code. -Let's note once again -- technically, any function can be used as a constructor. That is: any function can be run with `new`, and it will execute the algorithm above. The "capital letter first" is a common agreement, to make it clear that a function is to be run with `new`. +Let's note once again -- technically, any function (except arrow functions, as they don't have `this`) can be used as a constructor. It can be run with `new`, and it will execute the algorithm above. The "capital letter first" is a common agreement, to make it clear that a function is to be run with `new`. ````smart header="new function() { ... }" -If we have many lines of code all about creation of a single complex object, we can wrap them in constructor function, like this: +If we have many lines of code all about creation of a single complex object, we can wrap them in an immediately called constructor function, like this: ```js -let user = new function() { +// create a function and immediately call it with new +let user = new function() { this.name = "John"; this.isAdmin = false; @@ -80,7 +81,7 @@ let user = new function() { }; ``` -The constructor can't be called again, because it is not saved anywhere, just created and called. So this trick aims to encapsulate the code that constructs the single object, without future reuse. +This constructor can't be called again, because it is not saved anywhere, just created and called. So this trick aims to encapsulate the code that constructs the single object, without future reuse. ```` ## Constructor mode test: new.target diff --git a/1-js/04-object-basics/09-object-toprimitive/article.md b/1-js/04-object-basics/09-object-toprimitive/article.md index 18348ab92..e6fcca08d 100644 --- a/1-js/04-object-basics/09-object-toprimitive/article.md +++ b/1-js/04-object-basics/09-object-toprimitive/article.md @@ -3,7 +3,24 @@ What happens when objects are added `obj1 + obj2`, subtracted `obj1 - obj2` or printed using `alert(obj)`? -There are special methods in objects that do the conversion. +JavaScript doesn't exactly allow to customize how operators work on objects. Unlike some other programming languages, such as Ruby or C++, we can't implement a special object method to handle an addition (or other operators). + +In case of such operations, objects are auto-converted to primitives, and then the operation is carried out over these primitives and results in a primitive value. + +That's an important limitation, as the result of `obj1 + obj2` can't be another object! + +E.g. we can't make objects representing vectors or matrices (or archievements or whatever), add them and expect a "summed" object as the result. Such architectural feats are automatically "off the board". + +So, because we can't do much here, there's no maths with objects in real projects. When it happens, it's usually because of a coding mistake. + +In this chapter we'll cover how an object converts to primitive and how to customize it. + +We have two purposes: + +1. It will allow us to understand what's going on in case of coding mistakes, when such an operation happened accidentally. +2. There are exceptions, where such operations are possible and look good. E.g. subtracting or comparing dates (`Date` objects). We'll come across them later. + +## Conversion rules In the chapter we've seen the rules for numeric, string and boolean conversions of primitives. But we left a gap for objects. Now, as we know about methods and symbols it becomes possible to close it. @@ -13,15 +30,11 @@ The numeric conversion happens when we subtract objects or apply mathematical fu As for the string conversion -- it usually happens when we output an object like `alert(obj)` and in similar contexts. -## ToPrimitive +We can fine-tune string and numeric conversion, using special object methods. -When an object is used in the context where a primitive is required, for instance, in an `alert` or mathematical operations, it's converted to a primitive value using the `ToPrimitive` algorithm ([specification](https://tc39.github.io/ecma262/#sec-toprimitive)). +There are three variants of type conversion, that happen in various situations. -That algorithm allows us to customize the conversion using a special object method. - -Depending on the context, the conversion has a so-called "hint". - -There are three variants: +They're called "hints", as described in the [specification](https://tc39.github.io/ecma262/#sec-toprimitive): `"string"` : When an operation expects a string, for object-to-string conversions, like `alert`: @@ -88,11 +101,14 @@ Let's start from the first method. There's a built-in symbol named `Symbol.toPri ```js obj[Symbol.toPrimitive] = function(hint) { - // return a primitive value + // here goes the code to convert this object to a primitive + // it must return a primitive value // hint = one of "string", "number", "default" } ``` +If the method `Symbol.toPrimitive` exists, it's used for all hints, and no more methods are needed. + For instance, here `user` object implements it: ```js run @@ -117,12 +133,12 @@ As we can see from the code, `user` becomes a self-descriptive string or a money ## toString/valueOf -Methods `toString` and `valueOf` come from ancient times. They are not symbols (symbols did not exist that long ago), but rather "regular" string-named methods. They provide an alternative "old-style" way to implement the conversion. +If there's no `Symbol.toPrimitive` then JavaScript tries to find methods `toString` and `valueOf`: -If there's no `Symbol.toPrimitive` then JavaScript tries to find them and try in the order: +- For the "string" hint: `toString`, and if it doesn't exist, then `valueOf` (so `toString` has the priority for stirng conversions). +- For other hints: `valueOf`, and if it doesn't exist, then `toString` (so `valueOf` has the priority for maths). -- `toString -> valueOf` for "string" hint. -- `valueOf -> toString` otherwise. +Methods `toString` and `valueOf` come from ancient times. They are not symbols (symbols did not exist that long ago), but rather "regular" string-named methods. They provide an alternative "old-style" way to implement the conversion. These methods must return a primitive value. If `toString` or `valueOf` returns an object, then it's ignored (same as if there were no method). @@ -142,9 +158,9 @@ alert(user.valueOf() === user); // true So if we try to use an object as a string, like in an `alert` or so, then by default we see `[object Object]`. -And the default `valueOf` is mentioned here only for the sake of completeness, to avoid any confusion. As you can see, it returns the object itself, and so is ignored. Don't ask me why, that's for historical reasons. So we can assume it doesn't exist. +The default `valueOf` is mentioned here only for the sake of completeness, to avoid any confusion. As you can see, it returns the object itself, and so is ignored. Don't ask me why, that's for historical reasons. So we can assume it doesn't exist. -Let's implement these methods. +Let's implement these methods to customize the conversion. For instance, here `user` does the same as above using a combination of `toString` and `valueOf` instead of `Symbol.toPrimitive`: @@ -187,8 +203,7 @@ alert(user + 500); // toString -> John500 In the absence of `Symbol.toPrimitive` and `valueOf`, `toString` will handle all primitive conversions. - -## ToPrimitive and ToString/ToNumber +### A conversion can return any primitive type The important thing to know about all primitive-conversion methods is that they do not necessarily return the "hinted" primitive. @@ -262,4 +277,6 @@ The conversion algorithm is: 3. Otherwise if hint is `"number"` or `"default"` - try `obj.valueOf()` and `obj.toString()`, whatever exists. -In practice, it's often enough to implement only `obj.toString()` as a "catch-all" method for all conversions that return a "human-readable" representation of an object, for logging or debugging purposes. +In practice, it's often enough to implement only `obj.toString()` as a "catch-all" method for string conversions that should return a "human-readable" representation of an object, for logging or debugging purposes. + +As for math operations, JavaScript doesn't provide a way to "override" them using methods, so real life projects rarely use them on objects. \ No newline at end of file diff --git a/1-js/05-data-types/01-primitives-methods/article.md b/1-js/05-data-types/01-primitives-methods/article.md index 0c3a78299..de1990c59 100644 --- a/1-js/05-data-types/01-primitives-methods/article.md +++ b/1-js/05-data-types/01-primitives-methods/article.md @@ -41,8 +41,8 @@ Objetos são "mais pesados" que primitivos. Eles exigem recursos adicionais para Aqui está o paradoxo enfrentado pelo criador do JavaScript: -- Há muitas coisas que alguém poderia querer fazer com um primitivo como uma string ou um número. Seria ótimo acessá-los como métodos. -- Primitivos devem ser o mais rápido e leve possível. +- Há muitas coisas que alguém poderia querer fazer com um primitivo como uma string ou um número. Seria ótimo acessá-los usando métodos. +- Primitivos devem ser o mais rápidos e leves possível. A solução parece um pouco estranha, mas aqui está: diff --git a/1-js/05-data-types/03-string/article.md b/1-js/05-data-types/03-string/article.md index d5bda9690..3b07eccad 100644 --- a/1-js/05-data-types/03-string/article.md +++ b/1-js/05-data-types/03-string/article.md @@ -526,7 +526,7 @@ Now it becomes obvious why `a > Z`. The characters are compared by their numeric code. The greater code means that the character is greater. The code for `a` (97) is greater than the code for `Z` (90). - All lowercase letters go after uppercase letters because their codes are greater. -- Some letters like `Ö` stand apart from the main alphabet. Here, it's code is greater than anything from `a` to `z`. +- Some letters like `Ö` stand apart from the main alphabet. Here, its code is greater than anything from `a` to `z`. ### Correct comparisons [#correct-comparisons] diff --git a/1-js/05-data-types/04-array/article.md b/1-js/05-data-types/04-array/article.md index d722341bb..a86dead64 100644 --- a/1-js/05-data-types/04-array/article.md +++ b/1-js/05-data-types/04-array/article.md @@ -379,9 +379,7 @@ alert( arr[0] ); // undefined! no elements. alert( arr.length ); // length 2 ``` -In the code above, `new Array(number)` has all elements `undefined`. - -To evade such surprises, we usually use square brackets, unless we really know what we're doing. +To avoid such surprises, we usually use square brackets, unless we really know what we're doing. ## Multidimensional arrays diff --git a/1-js/05-data-types/08-weakmap-weakset/article.md b/1-js/05-data-types/08-weakmap-weakset/article.md index 5adbced20..8d5a86981 100644 --- a/1-js/05-data-types/08-weakmap-weakset/article.md +++ b/1-js/05-data-types/08-weakmap-weakset/article.md @@ -1,6 +1,6 @@ # WeakMap and WeakSet -As we know from the chapter , JavaScript engine stores a value in memory while it is reachable (and can potentially be used). +As we know from the chapter , JavaScript engine keeps a value in memory while it is "reachable" and can potentially be used. For instance: ```js diff --git a/1-js/05-data-types/09-keys-values-entries/article.md b/1-js/05-data-types/09-keys-values-entries/article.md index 1820bae80..9aba9e4ea 100644 --- a/1-js/05-data-types/09-keys-values-entries/article.md +++ b/1-js/05-data-types/09-keys-values-entries/article.md @@ -99,4 +99,4 @@ let doublePrices = Object.fromEntries( alert(doublePrices.meat); // 8 ``` -It may look difficult from the first sight, but becomes easy to understand after you use it once or twice. We can make powerful chains of transforms this way. +It may look difficult at first sight, but becomes easy to understand after you use it once or twice. We can make powerful chains of transforms this way. 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 9ce0a90f2..12f4e654b 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 @@ -306,18 +306,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); // pass an array as list with spread syntax -func.apply(context, args); // is same as using call +func.call(context, ...args); +func.apply(context, args); ``` -There's only a subtle difference: +They perform the same call of `func` with given context and arguments. + +There's only a subtle difference regarding `args`: - The spread syntax `...` allows to pass *iterable* `args` as the list to `call`. - The `apply` accepts only *array-like* `args`. -So, where we expect an iterable, `call` works, and where we expect an array-like, `apply` works. - -And for objects that are both iterable and array-like, like a real array, we can use any of them, but `apply` will probably be faster, because most JavaScript engines internally optimize it better. +...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. Passing all arguments along with the context to another function is called *call forwarding*. diff --git a/1-js/08-prototypes/01-prototype-inheritance/article.md b/1-js/08-prototypes/01-prototype-inheritance/article.md index 724d55d21..946a4c4a1 100644 --- a/1-js/08-prototypes/01-prototype-inheritance/article.md +++ b/1-js/08-prototypes/01-prototype-inheritance/article.md @@ -12,7 +12,7 @@ In JavaScript, objects have a special hidden property `[[Prototype]]` (as named ![prototype](object-prototype-empty.svg) -When we read a property from `object`, and it's missing, JavaScript automatically takes it from the prototype. In programming, such thing is called "prototypal inheritance". And soon we'll study many examples of such inheritance, as well as cooler language features built upon it. +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 property `[[Prototype]]` is internal and hidden, but there are many ways to set it. 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 0073e252e..372d50dd6 100644 --- a/1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/solution.md +++ b/1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/solution.md @@ -38,7 +38,12 @@ 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 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. +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. -At the end, we have `let user2 = new Object('Pete')`. The built-in `Object` constructor ignores arguments, it always creates an empty object, similar to `let user2 = {}`, that's what we have in `user2` after all. +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 diff --git a/1-js/09-classes/01-class/article.md b/1-js/09-classes/01-class/article.md index 8d4731e1b..d19b9ca9a 100644 --- a/1-js/09-classes/01-class/article.md +++ b/1-js/09-classes/01-class/article.md @@ -144,7 +144,7 @@ The result of this definition is about the same. So, there are indeed reasons wh Still, there are important differences. -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. +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. The language checks for that property in a variety of places. For example, unlike a regular function, it must be called with `new`: 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 2d47464c4..e26d0ec81 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 @@ -192,7 +192,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 `#checkWater`: +For instance, here's a private `#waterLimit` property and the water-checking private method `#fixWaterAmount`: ```js run class CoffeeMachine { diff --git a/1-js/11-async/02-promise-basics/article.md b/1-js/11-async/02-promise-basics/article.md index a8fb3d9e7..f2f533220 100644 --- a/1-js/11-async/02-promise-basics/article.md +++ b/1-js/11-async/02-promise-basics/article.md @@ -28,10 +28,10 @@ Its arguments `resolve` and `reject` are callbacks provided by JavaScript itself When the executor obtains the result, be it soon or late, doesn't matter, it should call one of these callbacks: -- `resolve(value)` — if the job finished successfully, with result `value`. -- `reject(error)` — if an error occurred, `error` is the error object. +- `resolve(value)` — if the job is finished successfully, with result `value`. +- `reject(error)` — if an error has occurred, `error` is the error object. -So to summarize: the executor runs automatically and attempts to perform a job. When it is finished with the attempt it calls `resolve` if it was successful or `reject` if there was an error. +So to summarize: the executor runs automatically and attempts to perform a job. When it is finished with the attempt, it calls `resolve` if it was successful or `reject` if there was an error. The `promise` object returned by the `new Promise` constructor has these internal properties: diff --git a/1-js/11-async/07-microtask-queue/article.md b/1-js/11-async/07-microtask-queue/article.md index b146c26b8..014dd93c0 100644 --- a/1-js/11-async/07-microtask-queue/article.md +++ b/1-js/11-async/07-microtask-queue/article.md @@ -30,7 +30,7 @@ As stated in the [specification](https://tc39.github.io/ecma262/#sec-jobs-and-jo - The queue is first-in-first-out: tasks enqueued first are run first. - Execution of a task is initiated only when nothing else is running. -Or, to say more simply, when a promise is ready, its `.then/catch/finally` handlers are put into the queue; they are not executed yet. When the JavaScript engine becomes free from the current code, it takes a task from the queue and executes it. +Or, to put it more simply, when a promise is ready, its `.then/catch/finally` handlers are put into the queue; they are not executed yet. When the JavaScript engine becomes free from the current code, it takes a task from the queue and executes it. That's why "code finished" in the example above shows first. @@ -40,7 +40,7 @@ Promise handlers always go through this internal queue. If there's a chain with multiple `.then/catch/finally`, then every one of them is executed asynchronously. That is, it first gets queued, then executed when the current code is complete and previously queued handlers are finished. -**What if the order matters for us? How can we make `code finished` run after `promise done`?** +**What if the order matters for us? How can we make `code finished` appear after `promise done`?** Easy, just put it into the queue with `.then`: diff --git a/1-js/12-generators-iterators/2-async-iterators-generators/article.md b/1-js/12-generators-iterators/2-async-iterators-generators/article.md index 072e10472..d4e9f7861 100644 --- a/1-js/12-generators-iterators/2-async-iterators-generators/article.md +++ b/1-js/12-generators-iterators/2-async-iterators-generators/article.md @@ -384,6 +384,8 @@ An example of use (shows commit authors in console): } })(); + +// Note: If you are running this in an external sandbox, you'll need to paste here the function fetchCommits described above ``` That's just what we wanted. diff --git a/1-js/13-modules/01-modules-intro/article.md b/1-js/13-modules/01-modules-intro/article.md index e949e0d98..2bef1c341 100644 --- a/1-js/13-modules/01-modules-intro/article.md +++ b/1-js/13-modules/01-modules-intro/article.md @@ -57,7 +57,7 @@ To use modules, we must set the attribute ` ``` -If we really need to make a "global" in-browser variable, we can explicitly assign it to `window` and access as `window.user`. But that's an exception requiring a good reason. +```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. +``` ### A module code is evaluated only the first time when imported -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. +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. -That has important consequences. Let's look at them using examples: +The one-time evaluation has important consequences, that we should be aware of. + +Let's see a couple of 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: @@ -135,9 +148,11 @@ import `./alert.js`; // Module is evaluated! import `./alert.js`; // (nothing) ``` -In practice, top-level module code is mostly used for initialization. We create data structures, pre-fill them, and if we want something to be reusable -- export it. +The second import shows nothing, because the module has already been evaluated. + +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, a more advanced example. +Now, let's consider a deeper example. Let's say, a module exports an object: @@ -162,52 +177,67 @@ import {admin} from './admin.js'; alert(admin.name); // Pete *!* -// Both 1.js and 2.js imported the same object +// Both 1.js and 2.js reference the same admin object // Changes made in 1.js are visible in 2.js */!* ``` -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 . +As you can see, when `1.js` changes the `name` property in the imported `admin`, then `2.js` can see the new `admin.name`. + +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 modules will see that. + +**Such behavior is actually very convenient, because it allows us to *configure* modules.** -Such behavior allows us to *configure* modules on first import. We can setup its properties once, and then in further imports it's ready. +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. -For instance, the `admin.js` module may provide certain functionality, but expect the credentials to come into the `admin` object from outside: +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: ```js // 📁 admin.js -export let admin = { }; +export let config = { }; export function sayHi() { - alert(`Ready to serve, ${admin.name}!`); + alert(`Ready to serve, ${config.user}!`); } ``` -Now, 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: +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`: ```js // 📁 init.js -import {admin} from './admin.js'; -admin.name = "Pete"; +import {config} from './admin.js'; +config.user = "Pete"; ``` -```js -// 📁 other.js -import {admin, sayHi} from './admin.js'; +...Now the module `admin.js` is configured. + +Further importers can call it, and it correctly shows the current user: -alert(admin.name); // *!*Pete*/!* +```js +// 📁 another.js +import {sayHi} from './admin.js'; 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 ``` diff --git a/1-js/99-js-misc/04-reference-type/article.md b/1-js/99-js-misc/04-reference-type/article.md index 227253436..1ec378059 100644 --- a/1-js/99-js-misc/04-reference-type/article.md +++ b/1-js/99-js-misc/04-reference-type/article.md @@ -4,7 +4,7 @@ ```warn header="In-depth language feature" This article covers an advanced topic, to understand certain edge-cases better. -It's not important. Many experienced developers live fine without knowing it. Read on if you're want to know how things work under the hood. +It's not important. Many experienced developers live fine without knowing it. Read on if you want to know how things work under the hood. ``` A dynamically evaluated method call can lose `this`. diff --git a/2-ui/1-document/03-dom-navigation/article.md b/2-ui/1-document/03-dom-navigation/article.md index d63a4c11d..6b306a32f 100644 --- a/2-ui/1-document/03-dom-navigation/article.md +++ b/2-ui/1-document/03-dom-navigation/article.md @@ -205,7 +205,7 @@ alert( document.body.parentNode === document.documentElement ); // true ## 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 there 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 they 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. diff --git a/2-ui/1-document/10-size-and-scroll-window/article.md b/2-ui/1-document/10-size-and-scroll-window/article.md index 95a5cd48b..08a2f6576 100644 --- a/2-ui/1-document/10-size-and-scroll-window/article.md +++ b/2-ui/1-document/10-size-and-scroll-window/article.md @@ -73,6 +73,12 @@ alert('Current scroll from the left: ' + window.pageXOffset); These properties are read-only. +```smart header="Also available as `window` properties `scrollX` and `scrollY`" +For historical reasons, both properties exist, but they are the same: +- `window.pageXOffset` is an alias of `window.scrollX`. +- `window.pageYOffset` is an alias of `window.scrollY`. +``` + ## Scrolling: scrollTo, scrollBy, scrollIntoView [#window-scroll] ```warn diff --git a/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/task.md b/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/task.md index 2cccea020..378bd1f54 100644 --- a/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/task.md +++ b/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/task.md @@ -6,7 +6,7 @@ importance: 5 Make table cells editable on click. -- On click -- the cell should became "editable" (textarea appears inside), we can change HTML. There should be no resize, all geometry should remain the same. +- On click -- the cell should become "editable" (textarea appears inside), we can change HTML. There should be no resize, all geometry should remain the same. - Buttons OK and CANCEL appear below the cell to finish/cancel the editing. - Only one cell may be editable at a moment. While a `` is in "edit mode", clicks on other cells are ignored. - The table may have many cells. Use event delegation. diff --git a/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/task.md b/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/task.md index fc48c21ff..644d814d9 100644 --- a/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/task.md +++ b/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/task.md @@ -9,4 +9,5 @@ Focus on the mouse. Then use arrow keys to move it: [demo src="solution"] P.S. Don't put event handlers anywhere except the `#mouse` element. + P.P.S. Don't modify HTML/CSS, the approach should be generic and work with any element. diff --git a/2-ui/4-forms-controls/2-focus-blur/article.md b/2-ui/4-forms-controls/2-focus-blur/article.md index 9dfb292ae..2d11a5003 100644 --- a/2-ui/4-forms-controls/2-focus-blur/article.md +++ b/2-ui/4-forms-controls/2-focus-blur/article.md @@ -104,7 +104,7 @@ The best recipe is to be careful when using these events. If we want to track us ``` ## Allow focusing on any element: tabindex -By default many elements do not support focusing. +By default, many elements do not support focusing. The list varies a bit between browsers, but one thing is always correct: `focus/blur` support is guaranteed for elements that a visitor can interact with: `