diff --git a/1-js/1-getting-started/3-devtools/article.md b/1-js/1-getting-started/3-devtools/article.md index 9e0a87f5..6d0d4328 100644 --- a/1-js/1-getting-started/3-devtools/article.md +++ b/1-js/1-getting-started/3-devtools/article.md @@ -8,11 +8,11 @@ But in the browser, a user doesn't see the errors by default. So, if something g To see errors and get a lot of other useful information about scripts, browsers have embedded "developer tools". -**It is recommended to use Chrome or Firefox for the development.** +**Most often developers lean towards Chrome or Firefox for the development.** Other browsers also provide developer tools, but are usually in a "catching-up" position, compared to Chrome/Firefox which are the best. -If there is an error in the certain browser only, then we can use it's developer tools, but usually -- Chrome/Firefox. +If there is an error in the certain browser only, then we can always switch to it's developer tools for the concrete problem. Developer tools are really powerful, there are many features. On this stage let's just look how to open them, look at errors and run JavaScript commands. diff --git a/1-js/2-first-steps/01-hello-world/article.md b/1-js/2-first-steps/01-hello-world/article.md index e5555a15..bfab9428 100644 --- a/1-js/2-first-steps/01-hello-world/article.md +++ b/1-js/2-first-steps/01-hello-world/article.md @@ -1,11 +1,18 @@ # Hello, world! -In this chapter we'll create a simple script and see it working. +About 98% of the tutorial is core Javascript, that is platform-independant. So you'll be able to learn how to use Node.JS and other things based on that knowledge. + +But we need something like a "base environment" to run our scripts, and browser is probably a good choice. + +So we'll start with attaching a script to the webpage. For other environments like Node.JS there are other ways to run it. [cut] +[todo remove defer/async from here and move to 2nd part?] + ## The "script" tag +[todo need this? and special (need it too?)] ```smart header="What if I want to move faster?" In the case if you've developed with JavaScript already or have a lot of experience in another language, you can skip detailed explanatins and jump to . There you can find an essense of important features. diff --git a/1-js/2-first-steps/03-structure/article.md b/1-js/2-first-steps/03-structure/article.md index c62b47b5..6a1bb0c1 100644 --- a/1-js/2-first-steps/03-structure/article.md +++ b/1-js/2-first-steps/03-structure/article.md @@ -6,7 +6,7 @@ The first overall thing to know is the code structure. ## Statements -The code consists of [statements](https://en.wikipedia.org/wiki/Statement_(computer_science)) -- syntax constructs and commands to perform actions. +[Statements](https://en.wikipedia.org/wiki/Statement_(computer_science)) are syntax constructs and commands to perform actions. We've already seen a statement `alert('Hello, world!')`, which shows the message. @@ -36,9 +36,9 @@ alert( 'Hello' ) alert( 'World' ) ``` -In this case JavaScript interprets the line break as a splitter. Just as if there were a semicolon between the lines. +Here JavaScript interprets the line break as an "implicit" semicolon. That's also called an [automatic semicolon insertion](https://tc39.github.io/ecma262/#sec-automatic-semicolon-insertion). -**But it's important that "in most cases" does not mean "always"!** +**In most cases a newline implies a simicolon. But "in most cases" does not mean "always"!** There are cases when a newline does not mean a semicolon, for example: @@ -50,12 +50,12 @@ alert(3 + The code outputs `6`, because JavaScript does not insert semicolons here. It is intuitively obvious that if the line ends with a plus `"+"`, then it is an "incomplete expression". And in this case that's actually fine and comfortable. -**But there are situations where JavaScript "fails" to assume a semicolon break where it is really needed.** +**But there are situations where JavaScript "fails" to assume a semicolon where it is really needed.** Errors which come appear in such cases are quite hard to find and fix. ````smart header="An example of the error" -For a curious reader who might be interested in a concrete example, check this code out: +If you're curious to see a concrete example, check this code out: ```js run [1, 2].forEach(alert) @@ -63,9 +63,9 @@ For a curious reader who might be interested in a concrete example, check this c It shows `1` then `2`. -No need to think about the meaning of the brackets `[]` and `forEach` just for now -- it does not matter here. Let's just remember the result. +No need to think about the meaning of the brackets `[]` and `forEach`, for now -- it does not matter. Let's just remember the result. -Now we prepend an `alert` statement *not followed by a semicolon*: +Now we prepend the code with an `alert` statement *not followed by a semicolon*: ```js run no-beautify alert( "There will be an error" ) // shown @@ -77,16 +77,16 @@ Now if we run it, only the first `alert` is shown, and then an error. But everything's fine if we add a semicolon: ```js run alert( "All fine now" ); // shown -[1, 2].forEach(alert) // then this too +[1, 2].forEach(alert) // this works too ``` -The error in the former variant occurs because JavaScript engine does not assume a semicolon before square brackets `[...]`, so the code is actually treated as a one-line statement: +The error in the no-semicolon variant occurs because JavaScript engine does not assume a semicolon before square brackets `[...]`, so the code is actually treated as a one-line statement: ```js run no-beautify alert( "There will be an error" )[1, 2].forEach(alert) ``` -And in this particular case, that's just wrong. Hence the error. +And in this particular case, that's just wrong. There must be two independent statements. Hence the error. ```` @@ -154,5 +154,5 @@ Please, don't hesitate to comment your code. Comments increase the overall code footprint, but that's not a problem at all. There are many tools which minify the code before publishing to production server. They remove comments, so they do not appear in the working scripts. So, the comments do not have any negative effects on production at all. -Further in the tutorial we'll make more notes about how to write the code better, easier to read and maintain. We'll also talk more about comments. +Further in the tutorial we'll devote a special chapter to code style, also explaining how to write better comments. diff --git a/1-js/2-first-steps/04-strict-mode/article.md b/1-js/2-first-steps/04-strict-mode/article.md index 01e42031..4a0740d9 100644 --- a/1-js/2-first-steps/04-strict-mode/article.md +++ b/1-js/2-first-steps/04-strict-mode/article.md @@ -58,10 +58,10 @@ Looking ahead let's just note that `"use strict"` can be put at the start of a f It is recommended to always start a script with `"use strict"`, for the following reasons: -1. First, because all modern browsers support it, except Internet Explorer 9 and lower. +1. First, all modern browsers support it. Only outdated ones like Internet Explorer 9 and below do not. 2. Second, the modern JavaScript actually forces us into the strict mode. There are several modern language features like "classes" and "modules" that enable strict mode automatically. So, it's hard to evade it. -Here in the tutorial all code (except where said otherwise) works in `"use strict"`. but we'll still note the subtle differences of what happens if you forget it or if the visitor has an outdated browser. So you will also be able to write a code for IE8 and below if you'd like that. +Here in the tutorial all code (except where said otherwise) works in `"use strict"`. but we'll still note the subtle differences of what happens if you forget it or if the visitor has an outdated browser. So you will also be able to write a code that also works for old IE if you'd like that. ## Summary diff --git a/1-js/2-first-steps/05-variables/3-uppercast-constant/solution.md b/1-js/2-first-steps/05-variables/3-uppercast-constant/solution.md index a72bd2ab..f3a96c69 100644 --- a/1-js/2-first-steps/05-variables/3-uppercast-constant/solution.md +++ b/1-js/2-first-steps/05-variables/3-uppercast-constant/solution.md @@ -1,5 +1,5 @@ -We generally use upper case for constants that are "hard-coded" or, in other words, when the value is directly written into the code. +We generally use upper case for constants that are "hard-coded". Or, in other words, when the value is known prior to execution and directly written into the code. -In this code, `birthday` is just like that. So we could use the upper case for it. +In this code, `birthday` is exactly like that. So we could use the upper case for it. -In contrast, `age` is evaluated in run-time. Today we have one age, a year after we'll have another one. It is constant in a sense that it does not change through the code execution. But it is a bit "less of a constant" than `birthday`, so we should keep the lower case for it. \ No newline at end of file +In contrast, `age` is evaluated in run-time. Today we have one age, a year after we'll have another one. It is constant in a sense that it does not change through the code execution. But it is a bit "less of a constant" than `birthday`, it is calculated, so we should keep the lower case for it. \ No newline at end of file diff --git a/1-js/2-first-steps/05-variables/3-uppercast-constant/task.md b/1-js/2-first-steps/05-variables/3-uppercast-constant/task.md index 8458e654..5fd18f90 100644 --- a/1-js/2-first-steps/05-variables/3-uppercast-constant/task.md +++ b/1-js/2-first-steps/05-variables/3-uppercast-constant/task.md @@ -9,18 +9,16 @@ Examine the following code: ```js const birthday = '18.04.1982'; -const age = calculateAgeBasedOn(birthday); +const age = someCode(birthday); ``` -Here we have a constant `birthday` date and the `age` is calculated with the help of a function (its code is not provided cause it doesn't matter here). +Here we have a constant `birthday` date and the `age` is calculated from `birthday` with the help of some code (it is not provided for shortness, and because details don't matter here). -Would it be right to name both constants using the upper case? Like this: +Would it be right to use upper case for `birthday`? For `age`? Or even for both? ```js -const BIRTHDAY = '18.04.1982'; +const BIRTHDAY = '18.04.1982'; // make uppercase? -const AGE = calculateAgeBasedOn(BIRTHDAY); +const AGE = someCode(BIRTHDAY); // make uppercase? ``` -...Or we should use the upper case for only one of them? Or just use lower case everywhere? - diff --git a/1-js/2-first-steps/05-variables/article.md b/1-js/2-first-steps/05-variables/article.md index 0ec5cc81..477e17fe 100644 --- a/1-js/2-first-steps/05-variables/article.md +++ b/1-js/2-first-steps/05-variables/article.md @@ -1,8 +1,8 @@ # Variables -Most of the time, script needs to work with the information. +Most of the time, a script needs to work with the information. -If it's an online-shop -- that's going to be the goods and a shopping cart. If it's a chat -- visitors, messages and so on. +If it's an online-shop -- that's going to be the goods and a shopping cart. If it's a chat -- users, messages and so on. Variables are used to store the information. @@ -10,7 +10,7 @@ Variables are used to store the information. ## A variable -A [variable]("https://en.wikipedia.org/wiki/Variable_(computer_science)") is defined as a "named storage" for the information. We can use variables to store the goods, visitors etc. +A [variable]("https://en.wikipedia.org/wiki/Variable_(computer_science)") is a basic "named storage" for the information. We can use variables to store the goods, visitors etc. To create a variable in JavaScript, we need to use the `let` keyword. @@ -30,7 +30,7 @@ message = 'Hello'; // store the string */!* ``` -The string is now saved into the memory area assosiated with that variable. We can access it using the variable name: +The string is now saved into the memory area assosiated with the variable. We can access it using the variable name: ```js run let message; @@ -44,8 +44,9 @@ alert( message ); // shows the variable content To be concise we can merge the variable declaration and assignment into a single line: ```js run -let message = 'Hello!'; -alert( message ); // same as above +let message = 'Hello!'; // define the variable and assign the value + +alert( message ); // Hello! ``` We can also declare multiple variables in one line: @@ -54,7 +55,7 @@ We can also declare multiple variables in one line: let user = 'John', age = 25, message = 'Hello'; ``` -That might seem shorter, but it's recommended, for the sake of beter readability, to use a single line per variable. +That might seem shorter, but it's not recommended. For the sake of beter readability, please use a single line per variable. The rewritten code is a bit longer, but easier to read: @@ -64,6 +65,24 @@ let age = 25; let message = 'Hello'; ``` +Some people also write many variables like that: +```js no-beautify +let user = 'John', + age = 25, + message = 'Hello'; +``` + +...Or even in comma-first style: + +```js no-beautify +let user = 'John' + ,age = 25 + ,message = 'Hello'; +``` + +Technically, all these variants do the same. So, it's a matter of personal taste and aestetics. + + ````smart header="`var` instead of `let`" In older scripts you may also find another keyword: `var` instead of `let`: @@ -231,9 +250,9 @@ myBirthday = '01.01.2001'; // error, can't reassign the constant! When a programmer is sure that the variable should never change, he can use `const` to guarantee it, and also to clearly show that fact to everyone. -### Uppercases constants +### Uppercase constants -There is a widespread practice to use constants as aliases for difficult-to-remember and hard-coded prior to execution values. +There is a widespread practice to use constants as aliases for difficult-to-remember values that are known prior to execution. Such constants are named using capitals and underscores. @@ -250,10 +269,7 @@ let color = COLOR_ORANGE; alert( color ); // #FF7F00 ``` -`COLOR_ORANGE` is much easier to understand and remember than `"#FF7F00"`. Also it is much easier to make a typo in `"#FF7F00"` than in `COLOR_ORANGE`. - -The upper case is only used for constants that are "hard-coded" (written in the code before its execution). - +`COLOR_ORANGE` is much easier to remember than `"#FF7F00"`. Also it is much easier to mistype in `"#FF7F00"` than in `COLOR_ORANGE`. And when reading the code -- `COLOR_ORANGE` is much more meaningful. ## Name things right diff --git a/1-js/2-first-steps/07-types/article.md b/1-js/2-first-steps/07-types/article.md index c95698fd..32b16d96 100644 --- a/1-js/2-first-steps/07-types/article.md +++ b/1-js/2-first-steps/07-types/article.md @@ -49,7 +49,7 @@ Besides regular numbers there are so-called "special numeric values" which also ```smart header="Mathematical operations are safe" Doing maths is safe in JavaScript. We can do anything: divide by zero, treat non-numeric strings as numbers, etc. -The script will never die. At worst we'll get `NaN` as the result. +The script will never stop ("die") on that. At worst we'll get `NaN` as the result. ``` Special numeric values formally belong to the "number" type. Of course they are not numbers in a common sense of this word. @@ -175,9 +175,9 @@ A `key` is a string, `value` can be anything. For instance, here we create a `user` object with two properties: ```js -let user = { - name: "John", - age: 30 +let user = { // an object + name: "John", // key "name" has value "John" + age: 30 // key "age" has value 30 }; ``` @@ -209,21 +209,22 @@ delete user.age; ![user object 3](object-user-delete.png) -If the string which denotes the key (also called a "property name") has multiple words, then we should use square brackets notation to access it: - -```js -user["likes to swim?"] = true; -``` - -See, the dot requires the property name to be a valid variable identifier. That is: no spaces and other limitations. Square brackets work with any string. - +If the string which denotes the key (also called a "property name") has multiple words, then the dot notation won't work: ```js // this would give a syntax error user.likes to swim? = true; ``` -Another powerful feature of square bracket notation is that they allow to access a property by the name from the variable: +That's because, the dot requires the property name to be a valid variable identifier. That is: no spaces and other limitations. + +There's a "square bracket notation" that works with any string: + +```js +user["likes to swim?"] = true; +``` + +Square brackets are also the way to access a property by the name from the variable: ```js let key = "likes to swim?"; @@ -234,10 +235,41 @@ Here we have a variable `key` which contains the property name, probably evaluat Most of time, the dot is used to access object properties, but when we need a complex property name or to pass the name as a variable, then -- we go square brackets. -Javascript supports object inheritance. There are many types that are based on objects: `Date` for dates, `Array` for ordered data, `Error` for error-reporting and so on. So the word "object" is applicable to a variety of things. The term *plain objects* or just `Object` (capital first) is used to represent "basic" objects, the ones we create with `{ ... }`. +What we've just seen is called a "plain object", or just `Object`. + +There are many other kinds of objects in Javascript: + +- `Array` to store ordered data collections, +- `Date` to store the information about the date and time, +- `Error` to store the information about an error. +- ...And so on. + +Sometimes people say something like "Array type" or "Date type", but formally they are not types of their own, but belong to a single "object" data type. And they extend it in various ways. Objects in JavaScript are very powerful. Here we've just scratched the surface of the topic that is really huge. We'll be closely working with objects and learning more about them in further parts of the tutorial. +````smart header="A trailing comma" + +[todo: move to 4-object ?] +Experienced developers sometimes add one more comma to the end of an object, like this: + +```js +let user = { + name: "John", + age: 30, + isAdmin: true*!*,*/!* // extra comma +}; +``` + +That's called a "trailing comma" and is allowed by the language. + +Sometimes the reason is pure lazyness: when in the development process the last property becomes unneeded and is removed, the programmer forgets to delete the comma at the end of newly last one. + +But from the other side that "lazyness" is justified, because the same line can be safely moved between objects -- from the first position to the middle or to the last -- without bookkeeping commas. That's a good thing. + +Actual decision whether to add trailing commas or not depends on you. Some people like them, some find them ugly. +```` + ## Arrays As we’ve just seen, objects in Javascript store arbitrary keyed values. @@ -271,7 +303,7 @@ Please note that arrays do not form a separate language type. They are based on ## Symbol type -The `symbol` type is used in conjunction with objects. Probably we won't need them any time soon, but it's the 7th and the last type of the language, so we must mention it here for the sake of completeness. +The `symbol` type is used in conjunction with objects. Probably we won't need them soon, but it's the 7th and the last type of the language, so we must mention it here for the sake of completeness. A "symbol" represents an unique identifier with a given name. @@ -288,12 +320,14 @@ Symbols in JavaScript are different from symbols in Ruby language (if you are fa let id1 = Symbol("id"); let id2 = Symbol("id"); +*!* alert(id1 == id2); // false +*/!* ``` Symbols is a special primitive type used for identifiers, which are guaranteed to be unique. So, even if we create many symbols with the same name, they are still unique. -The use case for symbols is to create "concealed" properties of an object, which only make sense locally, that no other part of code can occasionally access or overwrite. +The use case for symbols is to create "concealed" properties of an object, that no other part of code can occasionally access or overwrite. For instance, if we want to store an "identifier" for the object `user`, we can create a symbol with the name `id` for it: @@ -309,7 +343,7 @@ Now let's imagine that another script wants to have his own "id" property inside No problem. It can create its own `Symbol("id")`. There will be no conflict, because symbols are always different, even if they have the same name. -Please note that in the same case if we used a string `"id"` instead of a symbol here, then there would be a conflict: +Please note if we used a string `"id"` instead of a symbol for the same purpose, then there could be a conflict: ```js run let user = { name: "John" }; @@ -330,16 +364,16 @@ Symbols are widely used by the JavaScript language itself to store "system" prop ## The typeof operator [#type-typeof] -The `typeof` operator returns the type of the argument. It's handy in the case when we want to process values of different types differently, or just to make a quick check. +The `typeof` operator returns the type of the argument. It's useful when we want to process values of different types differently, or just want to make a quick check. -It allows two forms of syntax: +It supports two forms of syntax: 1. As an operator: `typeof x`. 2. Function style: `typeof(x)`. -In other words, it works both with the brackets or without them. They result is the same. +In other words, it works both with the brackets or without them. The result is the same. -The result of `typeof x` is a string, which has the type name: +The call to `typeof x` returns a string, which has the type name: ```js typeof undefined // "undefined" @@ -367,10 +401,10 @@ typeof alert // "function" (3) */!* ``` -Please note the last lines. +The last three lines may be a little unobvious so here's explanations: -1. The array is not a type of its own, but a subtype of object, that's why `typeof []` is `"object"`. -2. The result of `typeof null` equals to `"object"`. That's wrong. It is an officially recognized error in `typeof` implementation, kept for compatibility. Of course, `null` is not an object. It is a special value with a separate type of its own. +1. The array is not a type of its own, it is based on object, that's why `typeof []` is `"object"`. +2. The result of `typeof null` equals to `"object"`. That's wrong. It is an officially recognized error in `typeof`, kept for compatibility. Of course, `null` is not an object. It is a special value with a separate type of its own. So, again, that's an error in the language. 3. The result of `typeof alert` is `"function"`, because `alert` is a function of the language. We'll study functions in the near future and see that actually functions belong to the object type. But `typeof` treats them differently. That's very convenient in practice. diff --git a/1-js/2-first-steps/08-type-conversions/article.md b/1-js/2-first-steps/08-type-conversions/article.md index 9b0ac980..a9410a40 100644 --- a/1-js/2-first-steps/08-type-conversions/article.md +++ b/1-js/2-first-steps/08-type-conversions/article.md @@ -101,35 +101,45 @@ alert( '1' + 2 ); // '12' (string to the left) alert( 1 + 2 ); // 3, numbers (for the contrast) ``` -That only happens when one of arguments is a string, in other cases values are converted to numbers. +That only happens when one of arguments is a string. Otherwise values are converted to numbers. ```` ## ToBoolean Boolean conversion is the simplest one. -It happens in logical operations (later we'll meet `if` tests and other kinds), but also can be performed manually with the call of `Boolean(value)`. +It happens in logical operations (later we'll meet condition tests and other kinds), but also can be performed manually with the call of `Boolean(value)`. The conversion rule: - Values that are intuitively "empty", like `0`, an empty string, `null`, `undefined` and `NaN` become `false`. - Other values become `true`. +For instance: + +```js run +alert( Boolean(1) ); // true +alert( Boolean(0) ); // false + +alert( Boolean("hello") ); // true +alert( Boolean("") ); // false +``` + ````warn header="Please note: the string with zero `\"0\"` is `true`" Some languages (namely PHP) treat `"0"` as `false`. But in JavaScript a non-empty string is always `true`. ```js run alert( Boolean("0") ); // true -alert( Boolean(" ") ); // any non-empty string, even whitespaces are true +alert( Boolean(" ") ); // also true (any non-empty string is true) ``` ```` -````warn header="Empty objects and arrays are truthy" +````warn header="Empty objects are truthy" All objects become `true`: ```js run -alert( Boolean([]) ); // true -alert( Boolean({}) ); // true +alert( Boolean([]) ); // true, even if empty array +alert( Boolean({}) ); // true, even if empty object ``` ```` @@ -138,9 +148,9 @@ alert( Boolean({}) ); // true A string or numeric conversion of an object is a two-stage process. The object is first converted to a primitive value, and then ToString/ToNumber rules are applied to it. -The conversion is customizable on a per-object basis, so we'd better deal with it later when we know more about objects. +The conversion is customizable on a per-object basis, so we'll study it later when we go deeper into objects. [todo in the chapter?] -For now, let's just see two common rules that we often meet when showing objects. +Examples: - When a plain object is converted into a string, is becomes `[object Object]`: @@ -160,8 +170,8 @@ For now, let's just see two common rules that we often meet when showing objects We'll return to it in the chapter [todo]. -```smart header="It was only about ToString/ToNumber" -For ToBoolean, there is no complexity neither customizability. +```smart header="ToBoolean is always true" +Here `ToBoolean` was not mentioned, because it provides no customizability for objects The rule is simple: all objects are truthy. ``` diff --git a/1-js/2-first-steps/09-operators/article.md b/1-js/2-first-steps/09-operators/article.md index ce0bb231..c3ddeef9 100644 --- a/1-js/2-first-steps/09-operators/article.md +++ b/1-js/2-first-steps/09-operators/article.md @@ -214,6 +214,28 @@ alert( 8 % 3 ); // 2 is a remainder of 8 divided by 3 alert( 6 % 3 ); // 0 is a remainder of 6 divided by 3 ``` +## Exponentiation ** + +The exponentiation operator `**` is a recent addition to the language. Not all browsers support it yet. + +For a natural number `b`, the result of `a ** b` is `a` multiplied by itself `b` times. + +For instance: + +```js run +alert( 2 ** 2 ); // 4 (2 * 2) +alert( 2 ** 3 ); // 8 (2 * 2 * 2) +alert( 2 ** 4 ); // 16 (2 * 2 * 2 * 2) +``` + +The operator works with non-integer numbers of `a` and `b` as well, for instance: + +```js run +alert( 4 ** (1/2) ); // 2 (square root of 4) +alert( 8 ** (1/3) ); // 2 (cubic root of 8) +``` + + ## Increment/decrement: ++, -- Increasing or decreasing a number by one is among the most common numerical operations. @@ -341,7 +363,7 @@ The list of operators: - RIGHT SHIFT ( `>>` ) - ZERO-FILL RIGHT SHIFT ( `>>>` ) -These operators are used very rarely. To understand them, we should delve into low-level number representation, and it would not be optimal to do that right now. Especially because we won't need them any time soon. If you're curious, you can read the [Bitwise Operators](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators) article in MDN. But it would be more practical to return to this topic later when a real need arises. +These operators are used very rarely. To understand them, we should delve into low-level number representation, and it would not be optimal to do that right now. Especially because we won't need them any time soon. If you're curious, you can read the [Bitwise Operators](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators) article in MDN. But it would be more practical to do that when a real need arises. ## Modify-in-place diff --git a/1-js/2-first-steps/10-comparison/article.md b/1-js/2-first-steps/10-comparison/article.md index 2dff9746..75e69602 100644 --- a/1-js/2-first-steps/10-comparison/article.md +++ b/1-js/2-first-steps/10-comparison/article.md @@ -144,31 +144,33 @@ Let's see more corner cases. There's a non-intuitive behavior when `null` or `undefined` is compared with other values. -- For a strict equality check `===` these values are different, because each of them belong to a separate type of it's own. -- For a non-strict check `null == undefined`, there's a special rule. These two are a "sweet couple". They equal each other (in the sense of `==`), but no any other value. +For a strict equality check `===` +: These values are different, because each of them belong to a separate type of it's own. -- For maths and evaluation of other comparisons including `<`, `>`, `<=`, `>=`, values `null/undefined` are converted to a number: `null` becomes `0`, while `undefined` becomes `NaN`. +For a non-strict check `null == undefined` +: There's a special rule. These two are a "sweet couple": they equal each other (in the sense of `==`), but no any other value. -Now let's see funny things that happen when we apply those rules. And then, later, how do not fall into a trap with unobvious language features. +For maths and evaluation of other comparisons `< > <= >=` +: Values `null/undefined` are converted to a number: `null` becomes `0`, while `undefined` becomes `NaN`. + +Now let's see funny things that happen when we apply those rules. And, what's more important, how do not fall into a trap with unobvious language features. ### Strange result: null vs 0 Let's compare `null` with a zero: ```js run -alert( null > 0 ); // false -alert( null == 0 ); // false -alert( null >= 0 ); // *!*true*/!* +alert( null > 0 ); // (1) false +alert( null == 0 ); // (2) false +alert( null >= 0 ); // (3) *!*true*/!* ``` Yeah, mathematically that's strange. The last result states that "`null` is equal or greater than zero". Then one of the comparisons above must be correct, but they are both falsy. -Well, every programmer language has its own unobvious features. That was an example for Javascript. +The reason is that an equality check `==` and comparisons `> < >= <=` work differently. Comparisons convert `null` to a number, hence treat it as `0`. That's why (1) `null >= 0` is true and (3) `null > 0` is false. -The reason is that an equality check `==` and comparisons `> < >= <=` work differently. Comparisons convert `null` to a number, hence treat it as `0`. That's why `null >= 0` is true and `null > 0` is false. - -From the other hand, equality `==` for `undefined` and `null` works without any conversions. There's just a rule that they equal each other and don't equal anything else. That's why `null == 0` is false. +From the other hand, the equality check `==` for `undefined` and `null` works by the rule, without any conversions. They equal each other and don't equal anything else. That's why (2) `null == 0` is false. ### An uncomparable undefined @@ -195,19 +197,6 @@ Just treat any comparison with `undefined/null` except the strict equality `===` Don't use comparisons `>= > < <=` with a variable which may be `null/undefined`, unless you are really sure what you're doing. If a variable can have such values, then check it separately. -## Comparison with objects - -For equality checks two objects are always treated as non-equal. - -```js run -alert( {} == {} ); // false -alert( [] == [] ); // false -``` - -Note that Javascript does not try to compare the objects by content. The rule is simple: different objects are not equal. - -For other comparisons like `< >` operators, or when an object is compared with a primitive, objects are first converted to primitives, and then the comparison runs as usual. - ## Summary - Comparison operators return a logical value. diff --git a/1-js/2-first-steps/12-uibasic/article.md b/1-js/2-first-steps/12-uibasic/article.md index 41db3896..783c936a 100644 --- a/1-js/2-first-steps/12-uibasic/article.md +++ b/1-js/2-first-steps/12-uibasic/article.md @@ -2,6 +2,8 @@ This chapter covers basic UI operations: `alert`, `prompt` and `confirm`. They allow to ask a visitor for the input and show the results. +They are browser-specific. For other environments like Node.JS there are other ways of getting the information. Also they are very simple, so we can use them for the start. + [cut] ## alert diff --git a/1-js/4-data-structures/4-object/article.md b/1-js/4-data-structures/4-object/article.md index 5cc1f17b..c5f8ca93 100644 --- a/1-js/4-data-structures/4-object/article.md +++ b/1-js/4-data-structures/4-object/article.md @@ -3,7 +3,7 @@ Objects in JavaScript combine two functionalities. 1. First -- they are "associative arrays": a structure for storing keyed data. -2. Second -- they provide features for object-oriented programming. +2. Second -- they provide features for object-oriented programming like inheritance. Here we concentrate on the first part: using objects as a data store, and we will study it in-depth. That's the required base for studying the second part. @@ -41,14 +41,26 @@ let user = { ![](object-user-props.png) + +````smart header="Trailing comma" +The last property may end with a comma: +```js +let user = { + name: "John", + age: 30*!*,*/!* +} +``` +That is called a "trailing" or "hanging" comma. Makes it easier to add/move/remove properties, because all lines become alike. +```` + +## Accessing a property + To access a property, there are two syntaxes: - The dot notation: `user.name` - Square brackets: `user["name"]` -Square brackets are more powerful, because they allow to specify arbitrary string as a property name. In contrast, the dot notation requires the nae to be a valid variable identifier, that is: no spaces, special chracters etc. - -But more than that, square brackets is the only choice when the name of the property is in a variable. +The dot notation requires the name to be a valid variable identifier, that is: no spaces, special chracters etc. Square brackets are more powerful, because they allow to specify an arbitrary string as a property name. Also, square brackets is the only choice when the name of the property is in a variable. For instance: @@ -60,12 +72,13 @@ let user = { let key = prompt("What do you want to know about the user?", "name"); -alert( user[key] ); // John (if enter "name"), 30 for the "age" +// access by variable +alert( user[key] ); // John (if enter "name") ``` -The square brackets literally say: "take the property name from the variable". +The square brackets mean: "take the property name from the variable". -Also it is handy to use square brackets in an object literal, when the property name is stored in a variable. +Square brackets also can be used in an object literal. That's called a *computed property*: @@ -73,7 +86,7 @@ That's called a *computed property*: let fruit = prompt("Which fruit to buy?", "apple"); let bag = { - [fruit]: 5, + [fruit]: 5, // the name of the property is taken from the variable fruit }; alert( bag.apple ); // 5 if fruit="apple" @@ -107,21 +120,12 @@ let obj = { 0: "test" // same as "0": "test" } +// bot alerts access the same property (the number 0 is converted to string "0") alert( obj["0"] ); // test alert( obj[0] ); // test (same property) ``` ```` -````smart header="Trailing comma" -The last property may end with a comma: -```js -let user = { - name: "John", - age: 30*!*,*/!* -} -``` -That is called a "trailing" or "hanging" comma. Makes it easier to add/move/remove property, because all lines become alike. -```` ````smart header="Reserved words are allowed as property names" A variable cannot have a name equal to one of language-reserved words like "for", "let", "return" etc. @@ -138,7 +142,9 @@ let obj = { alert( obj.for + obj.let + obj.return ); // 6 ``` -Basically, any name is allowed. With one exclusion. There's a built-in property named `__proto__` with a special functionality (we'll cover it later), which can't be set to a non-object value: +Basically, any name is allowed, with one exclusion: `__proto__`. + +The built-in property named `__proto__` has a special functionality (we'll cover it later), and it can't be set to a non-object value: ```js run let obj = {}; @@ -146,7 +152,7 @@ obj.__proto__ = 5; alert(obj.__proto__); // [object Object], didn't work as intended ``` -If we want to store *arbitrary* (user-provided) keys, then this can be a source of bugs. There's another data structure [Map](info:map-set-weakmap-weakset), that we'll learn in a few chapters, it can support arbitrary keys. +As you we see from the code, an assignment to a primitive is ignored. If we want to store *arbitrary* (user-provided) keys, then such behavior can be the source of bugs and even vulnerabilities, because it's unexpected. There's another data structure [Map](info:map-set-weakmap-weakset), that we'll learn in the chapter , which supports arbitrary keys. ```` ## Removing a property @@ -194,23 +200,25 @@ let key = "age"; alert( key in user ); // true, takes the value of key and checks for such property ``` -````smart header="The property which equals `undefined`" -Thee is a case when `"=== undefined"` check fails, but the `"in"` operator works correctly. +````smart header="Using \"in\" for properties that store `undefined`" +There is a case when `"=== undefined"` check fails, but the `"in"` operator works correctly. It's when an object property stores `undefined`: ```js run -let obj = { test: undefined }; +let obj = { + test: undefined +}; alert( obj.test ); // undefined, no such property? alert( "test" in obj ); // true, the property does exist! ``` -In the code above, the property `obj.test` stores `undefined`, so the first check fails. -But the `in` operator puts things right. -Situations like this happen very rarely, because `undefined` is usually not assigned. We mostly use `null` for unknown values. So the `in` operator is an exotic guest in the code. +In the code above, the property `obj.test` technically exists. So the `in` operator works right. + +Situations like this happen very rarely, because `undefined` is usually not assigned. We mostly use `null` for "unknown" or "empty" values. So the `in` operator is an exotic guest in the code. ```` ## Property shorthands @@ -245,7 +253,6 @@ Methods definitions }; ``` - To say the truth, these notations are not fully identical. There are subtle differences related to object inheritance (to be covered later), but for now they do not matter. ## Loops @@ -395,12 +402,37 @@ alert(*!*user.name*/!*); // 'Pete', changes are seen from the "user" reference Quite obvious, if we used one of the keys (`admin`) and changed something inside the cabinet, then if we use another key later (`user`), we find things modified. +### Comparison with objects -## Cloning objects +Two objects are equal only when they are one object: + +```js run +let a = {}; +let b = a; // copy the reference + +alert( a == b ); // true, both variables reference the same object +``` + +We can also think of it like: the variables are "papers with address" of the objects. We copied the address from `a` to `b`. Then when we compare `a == b`, we compare the adresses. If they match, the equality is truthy. + +In all other cases objects are non-equal, even if their content is the same. + +For instance: + +```js run +let a = {}; +let b = {}; // two independents object + +alert( a == b ); // false +``` + +For unusual equality checks like: object vs a priimtive, or an object less/greater `< >` than another object, objects are converted to numbers. To say the truth, such comparisons occur very rarely in real code and usually are a result of a mistake. + +## Cloning, Object.assign What if we need to duplicate an object? Create an independant copy, a clone? -That's also doable, but a little bit more difficult, because there's no such method in Javascript. Frankly, that's very rarely needed. +That's also doable, but a little bit more difficult, because there's no such method in Javascript. Actually, copying by reference is good most of the time. But if we really want that, then we need to create a new object and replicate the structure of the existing one by iterating over its properties and copying them on the primitive level. @@ -498,14 +530,14 @@ Now it's not enough to copy `clone.sizes = user.sizes`, because the `user.sizes` To fix that, we should examine the value of `user[key]` in the cloning loop and if it's an object, then replicate it's structure as well. That is called a "deep cloning". -There's a standard algorithm for deep cloning that handles the case above and more complex cases, called the [Structured cloning algorithm](w3c.github.io/html/infrastructure.html#internal-structured-cloning-algorithm). We can use a ready implementation from the Javascript library [lodash](https://lodash.com). The method is [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep). +There's a standard algorithm for deep cloning that handles the case above and more complex cases, called the [Structured cloning algorithm](w3c.github.io/html/infrastructure.html#internal-structured-cloning-algorithm). We can use a ready implementation of it from the Javascript library [lodash](https://lodash.com). The method is called [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep). ## Ordering -Are objects ordered? If we loop over an object, do we get all properties in the same order that they are in the `{...}` definition? +Are objects ordered? In other words, if we loop over an object, do we get all properties in the same order that they are added in it? -The answer is "yes" for non-numeric properties, "no" for others. +The short answer is: "no" for integer properties, "yes" for others. The details follow. As an example, let's consider an object with the phone codes: @@ -519,29 +551,40 @@ let codes = { }; *!* -for(let code in codes) alert(code); // 1, 41, 44, 49 +for(let code in codes) { + alert(code); // 1, 41, 44, 49 +} */!* ``` -The object is used to generate HTML `