diff --git a/1-js/2-first-steps/07-types/article.md b/1-js/2-first-steps/07-types/article.md index b7e05eb7..7a4f0824 100644 --- a/1-js/2-first-steps/07-types/article.md +++ b/1-js/2-first-steps/07-types/article.md @@ -127,15 +127,15 @@ let age = null; In JavaScript `null` is not a "reference to a non-existing object" or a "null pointer" like in some other languages. -It's just a special value which has the sense of "nothing" or "value unknown". +It's just a special value which has the sense of "nothing", "empty" or "value unknown". -The code above states that the `age` is unknown. +The code above states that the `age` is unknown or empty for some reason. ## The "undefined" value The special value `undefined` stands apart. It makes a type of its own, just like `null`. -The sense of `undefined` is "value is not assigned". +The sense of `undefined` is "value is not assigned". If a variable is declared, but not assigned, then its value is exactly `undefined`: @@ -157,6 +157,7 @@ alert( x ); // "undefined" ...But it's not recommended to do that. Normally, we use `null` to write an "empty" or an "unknown" value into the variable, and `undefined` is only used for checks, to see if the variable is assigned or similar. + ## Objects The `object` type is special. diff --git a/1-js/2-first-steps/19-function-basics/1-if-else-required/solution.md b/1-js/2-first-steps/17-function-basics/1-if-else-required/solution.md similarity index 100% rename from 1-js/2-first-steps/19-function-basics/1-if-else-required/solution.md rename to 1-js/2-first-steps/17-function-basics/1-if-else-required/solution.md diff --git a/1-js/2-first-steps/19-function-basics/1-if-else-required/task.md b/1-js/2-first-steps/17-function-basics/1-if-else-required/task.md similarity index 100% rename from 1-js/2-first-steps/19-function-basics/1-if-else-required/task.md rename to 1-js/2-first-steps/17-function-basics/1-if-else-required/task.md diff --git a/1-js/2-first-steps/19-function-basics/2-rewrite-function-question-or/solution.md b/1-js/2-first-steps/17-function-basics/2-rewrite-function-question-or/solution.md similarity index 100% rename from 1-js/2-first-steps/19-function-basics/2-rewrite-function-question-or/solution.md rename to 1-js/2-first-steps/17-function-basics/2-rewrite-function-question-or/solution.md diff --git a/1-js/2-first-steps/19-function-basics/2-rewrite-function-question-or/task.md b/1-js/2-first-steps/17-function-basics/2-rewrite-function-question-or/task.md similarity index 100% rename from 1-js/2-first-steps/19-function-basics/2-rewrite-function-question-or/task.md rename to 1-js/2-first-steps/17-function-basics/2-rewrite-function-question-or/task.md diff --git a/1-js/2-first-steps/19-function-basics/3-min/solution.md b/1-js/2-first-steps/17-function-basics/3-min/solution.md similarity index 100% rename from 1-js/2-first-steps/19-function-basics/3-min/solution.md rename to 1-js/2-first-steps/17-function-basics/3-min/solution.md diff --git a/1-js/2-first-steps/19-function-basics/3-min/task.md b/1-js/2-first-steps/17-function-basics/3-min/task.md similarity index 100% rename from 1-js/2-first-steps/19-function-basics/3-min/task.md rename to 1-js/2-first-steps/17-function-basics/3-min/task.md diff --git a/1-js/2-first-steps/19-function-basics/4-pow/solution.md b/1-js/2-first-steps/17-function-basics/4-pow/solution.md similarity index 100% rename from 1-js/2-first-steps/19-function-basics/4-pow/solution.md rename to 1-js/2-first-steps/17-function-basics/4-pow/solution.md diff --git a/1-js/2-first-steps/19-function-basics/4-pow/task.md b/1-js/2-first-steps/17-function-basics/4-pow/task.md similarity index 100% rename from 1-js/2-first-steps/19-function-basics/4-pow/task.md rename to 1-js/2-first-steps/17-function-basics/4-pow/task.md diff --git a/1-js/2-first-steps/19-function-basics/article.md b/1-js/2-first-steps/17-function-basics/article.md similarity index 100% rename from 1-js/2-first-steps/19-function-basics/article.md rename to 1-js/2-first-steps/17-function-basics/article.md diff --git a/1-js/2-first-steps/19-function-basics/function_basics.png b/1-js/2-first-steps/17-function-basics/function_basics.png similarity index 100% rename from 1-js/2-first-steps/19-function-basics/function_basics.png rename to 1-js/2-first-steps/17-function-basics/function_basics.png diff --git a/1-js/2-first-steps/19-function-basics/function_basics@2x.png b/1-js/2-first-steps/17-function-basics/function_basics@2x.png similarity index 100% rename from 1-js/2-first-steps/19-function-basics/function_basics@2x.png rename to 1-js/2-first-steps/17-function-basics/function_basics@2x.png diff --git a/1-js/2-first-steps/20-function-parameters/article.md b/1-js/2-first-steps/18-function-parameters/article.md similarity index 100% rename from 1-js/2-first-steps/20-function-parameters/article.md rename to 1-js/2-first-steps/18-function-parameters/article.md diff --git a/1-js/2-first-steps/21-function-expression/article.md b/1-js/2-first-steps/19-function-create-advanced/article.md similarity index 78% rename from 1-js/2-first-steps/21-function-expression/article.md rename to 1-js/2-first-steps/19-function-create-advanced/article.md index 1b947519..6ffb7cab 100644 --- a/1-js/2-first-steps/21-function-expression/article.md +++ b/1-js/2-first-steps/19-function-create-advanced/article.md @@ -1,4 +1,4 @@ -# Function expressions +# Function expressions and more In JavaScript, a function is a value. @@ -17,7 +17,7 @@ This syntax is called a "Function Declaration". ```js let sayHi = function() { alert( "Hello" ); -} +}; ``` The latter syntax is called a "Function Expression". @@ -70,7 +70,7 @@ That's what happens above in detail: Note, that we could also have used a Function Expression to declare `sayHi`, in the first line: ```js -let sayHi = function() { ... } +let sayHi = function() { ... }; let func = sayHi; // ... @@ -78,6 +78,27 @@ let func = sayHi; Everything would work the same. Even more obvious what's going on, right? + +````smart header="Why semicolon?" +There might be a question, why Function Expression has a semicolon `;` at the end, and Function Declaration does not: + +```js +function sayHi() { + // ... +} + +let sayHi = function() { + // ... +}*!*;*/!* +``` + +The answer is simple: +- There's no need in `;` at the end of code blocks and syntax structures that use them like `if { ... }`, `for { }`, `function f { }` and alike. +- A Function Expression appears in the context of the statement: `let sayHi = value;`. It's not a code block. The semicolon `;` is recommended at the end of statements, no matter what is the `value`. So the semicolon here is not related to Function Expression itself in any way, it just terminates the statement. +```` + + + ## Function is an object Every value in Javascript has the type. What type of value is a function? @@ -135,7 +156,6 @@ A function can be perceived as an *action*. We can copy it between variables and run when we want. We can even add properties to it if we wish. ``` - ## Function Expression as a method Now let's step back and reconsider. We have two ways of declaring a function. Do we really need both? What's so good about Function Expressions that makes it useful? @@ -271,6 +291,7 @@ let sayHi = function(name) { // (*) no magic any more Function Expressions are created when the execution reaches them. That would happen only in the line `(*)`. Too late. + ### Function Declaration in a block When Function Declaration is made within a code block, it is visible everywhere inside that block. But not outside of it. @@ -389,6 +410,112 @@ It's also a little bit easier to look up Function Declarations in the code, they But if a Function Declaration does not fit for some reason (we've seen an example), then a Function Expression should be used. ``` + +## Named Function Expression + +Now the feature that exists for a Function Expression, but not for Function Declaration, and hence may be a reason to use them. + +**We can specify an "internal" name for a Function Expression.** + +Compare these two variants: + +```js +let sayHi = function() { // (1) + alert('Hello'); +}; + +let sayHi = function *!*func*/!*() { // (2) + alert('Hello'); +}; +``` + +Both create a function and put it into the variable `sayHi`. And usually we don't specify anything in the `function()`, so the 1st variant works fine. + +Although the function can move to another variable. As we've seen it's easy to copy a function and maybe replace the previous value with something else: + +```js run +let sayHi = function() { + alert('Hello'); +}; + +// oh maybe that's better? +let oldSayHi = sayHi; // keep the old variant here +sayHi = function() { // replace with a newer one + alert("What's up dude?"); +}; + + +oldSayHi(); // Hello +sayHi(); // What's up dude? +``` + +The problem may occur if a function references *itself* from inside. It happens when the function wants to access its properties (`sayHi.counter` in the example above), or sometimes it wants to call itself one more time. + + +But if the function has moved, then the old name is not relevant any move! + + +Let's see: + +```js run +// create a function +let sayHi = function() { + sayHi.counter++; + alert('Hi ' + sayHi.counter); +}; +sayHi.counter = 0; + +// move it +let movedSayHi = sayHi; + +// overwrite the old name to make things more obvious +sayHi = null; + +*!* +movedSayHi(); // Error: Cannot read property 'counter' of null +*/!* +``` + +The optional `name` which we can put into `function name()` is exactly meant to solve this kind of problems. + +- It is only visible from inside the function. +- It always references the current function. + +Let's use it to fix the code: + +```js run +// now with the internal name "say" +*!* +let sayHi = function say() { + say.counter++; + alert('Hi ' + say.counter); // and use it everywhere inside +}; +*/!* +sayHi.counter = 0; + +let movedSayHi = sayHi; + +sayHi = null; + +movedSayHi(); // Hi 1 +movedSayHi(); // Hi 2 (works) + +alert(say); // Error (say is undefined, that's normal) +``` + +Please note that: + +- The name `say` references the current function no matter where it is. That's why it works. +- The name `say` exists only inside the function. The last line demonstrates that. + +So the outer code still uses `sayHi` (or `movedSayHi` later). It doesn't see `say`, actually it doesn't need to see it. The `say` is an "internal function name", how it calls itself privately. + +A Function Expression with a name is called *Named Function Expression*. + +The "internal name" feature described here is only available for Function Expressions, not to Function Declarations. For Function Declarations, the `function name()` behaves like `sayHi`, there's just no syntax possibility to add a one more "internal" name for them. + + + ## Arrow functions [#arrow-functions] Enough with the complexities for now. Let's relax with another syntax of functions that can make our code shorter. @@ -504,11 +631,14 @@ It is used in very specific cases, like when we receive the code from the server - Functions are values. They can be assigned, copied or declared in any place of the code. - If the function is declared as a separate statement, in the main code flow -- that's called a Function Declaration. - If the function is created as a part of an expression -- it's a Function Expression. -- Function Declarations are processed before the code block is executed. They are visible everywhere in the block. Or in the whole script if not enclosed in a block. +- Function Declarations are processed before the code block is executed. They are visible everywhere in the block (or the script). - Function Expressions are created when the execution flow reaches them. +- Function Expressions allow to specify an optional name for internal needs (Named Function Expression). -If we simple want to create a function, then in most cases Function Declaration is preferable. Novice programmers sometimes overuse Function Expression by creating many functions with `let func = function()`, but compare, which code is more readable: +If we simple want to create a function, then in most cases Function Declaration is preferable. + +Novice programmers sometimes tend to overuse Function Expression by creating many functions with `let func = function()`, but compare, which code is more readable: ```js no-beautify let f = function() { /* expression */ } @@ -518,12 +648,14 @@ function f() { /* declaration */ } Function Declaration is shorter and more obvious. The additional bonus -- it can be called before the actual declaration. -**Use Function Expression to write elegant code when the function must be created at-place, inside another expression or when Function Declaration doesn't fit well for the task.** +**Use Function Expression only when Function Declaration does not fit the task.** + +We've seen a couple of examples of that in the chapter. And will see more in the future. We also touched two other ways to create a function: - Arrow functions are handy for one-liners. The come in two flavours: - 1. Without figure brackets: `(...args) => expression` -- returns the evaluated `expression. + 1. Without figure brackets: `(...args) => expression` -- returns the evaluated `expression`. 2. With brackets: `(...args) => { body }` -- need an explicit `return` statement to return something, but can be more complex. - `new Function(args, body)` diff --git a/1-js/2-first-steps/20-object-methods/article.md b/1-js/2-first-steps/20-object-methods/article.md new file mode 100644 index 00000000..6ffb7cab --- /dev/null +++ b/1-js/2-first-steps/20-object-methods/article.md @@ -0,0 +1,663 @@ +# Function expressions and more + +In JavaScript, a function is a value. + +We can declare it as we did before: + +```js +function sayHi() { + alert( "Hello" ); +} +``` + +This syntax is called a "Function Declaration". + +...But there is another way of creating a function: + +```js +let sayHi = function() { + alert( "Hello" ); +}; +``` + +The latter syntax is called a "Function Expression". + +The meaning of these code samples is the same: "create a function and put it into the variable `sayHi`". + +Let's stress: a function is not a "magical language structure", but a kind of value. Both syntaxes mean the same: create a special "function" value and put it into the variable. + +No matter, how the function is defined -- it's just a value, stored in the variable `sayHi`. + +We can even print out that value using `alert`: + +```js run +function sayHi() { + alert( "Hello" ); +} + +*!* +alert( sayHi ); // shows the function code +*/!* +``` + +Note that there are no brackets after `sayHi` in the last line, because we do not intend to run the function. There are programming languages where any mention of a function name causes it's call, but JavaScript is not like that. In JavaScript, a function is a value and we can deal with that as a value. The code above shows its string representation, that is its source code. + +It is a special value of course, in the sense that we can call it using brackets: `"sayHi()"`. + +But it's still a value. So we can work with it like with other kinds of values. + +We can copy a function to another variable: + +```js run no-beautify +function sayHi() { // (1) create + alert( "Hello" ); +} + +let func = sayHi; // (2) copy + +func(); // Hello // (3) call the copy (it works)! +sayHi(); // Hello // this works too (why wouldn't it) +``` + +That's what happens above in detail: + +1. Function Declaration `(1)` creates the function and puts it into the variable named `sayHi`. +2. Line `(2)` copies it into variable `func`. + + Please note again: there are no brackets after `sayHi`. If they were, then `func = sayHi()` would write *the result of the call* `sayHi()` into `func`, not *the function* `sayHi` itself. +3. Now the function can be called both as `sayHi()` and `func()`. + +Note, that we could also have used a Function Expression to declare `sayHi`, in the first line: + +```js +let sayHi = function() { ... }; + +let func = sayHi; +// ... +``` + +Everything would work the same. Even more obvious what's going on, right? + + +````smart header="Why semicolon?" +There might be a question, why Function Expression has a semicolon `;` at the end, and Function Declaration does not: + +```js +function sayHi() { + // ... +} + +let sayHi = function() { + // ... +}*!*;*/!* +``` + +The answer is simple: +- There's no need in `;` at the end of code blocks and syntax structures that use them like `if { ... }`, `for { }`, `function f { }` and alike. +- A Function Expression appears in the context of the statement: `let sayHi = value;`. It's not a code block. The semicolon `;` is recommended at the end of statements, no matter what is the `value`. So the semicolon here is not related to Function Expression itself in any way, it just terminates the statement. +```` + + + +## Function is an object + +Every value in Javascript has the type. What type of value is a function? + +In Javascript, a function is an object. + +For example, all functions have property `name` (function name) and `length` (number of arguments): + +```js run +function sayHi() { + alert("Hi"); +} + +alert( sayHi.name ); // sayHi +alert( sayHi.length ); // 0 +``` + +We can add our own properties to it as well: + +```js run +function sayHi() { + alert("Hi"); + + *!* + // let's count how many times we run + sayHi.counter++; + */!* +} +sayHi.counter = 0; // initial value + +sayHi(); // Hi +sayHi(); // Hi + +alert( `Called ${sayHi.counter} times` ); // Called 2 times +``` + + +```warn header="A property is not a variable" +A property assigned to a function like `sayHi.counter = 0` does *not* define a local variable `counter` inside it. In other words, a property `sayHi.counter` and `let counter` inside the function (if we have it) are two unrelated things. + +We can treat a function as an object for convenience, store properties in it, that has no effect on its execution. +``` + +There are many well-known Javascript libraries that make a great use of custom function properties. + +They create a "main" function and attach many other "helper" functions to it. For instance, the [jquery](https://jquery.com) library creates a function named `$`. The [lodash](https://lodash.com) library creates a function `_`. And then adds `_.clone`, `_.keyBy` and other properties to (see the [docs](https://lodash.com/docs) when you want learn more about them). + +So, a function can do a useful job by itself and also carry a bunch of other functionality in properties. + +```smart header="A function is a value representing an \"action\"" +Regular values like strings or numbers represent the *data*. + +A function can be perceived as an *action*. + +We can copy it between variables and run when we want. We can even add properties to it if we wish. +``` + +## Function Expression as a method + +Now let's step back and reconsider. We have two ways of declaring a function. Do we really need both? What's so good about Function Expressions that makes it useful? + +Actually, yes, we do. For example, we can assign functions to object properties using function expressions. + +As we remember from the chapter , objects are data structures meant to store collections of data. Most often, we create objects to represent entities of the real world, like users, goodies and so on: + +```js +let user = { + name: "John", + age: 30 +}; +``` + +In the real world, a user can `act`: to select something from the shopping cart, to login, to logout etc. For the start, let's teach him to say hello: + +```js run +let user = { + name: "John", + age: 30 +}; + +*!* +user.sayHi = function() { + alert("Hello!"); +}; +*/!* + +user.sayHi(); // Hello! +``` + +You see? We've just used a Function Expression to create the function and assign it to the property `user.sayHi` of the object. + +Then we can call it any time. The user now can speak! + +That is how a so-called "object-oriented code" is written. We make objects which reflect entities of the real world: like a user, or a document, or a button that is clickable etc. + +An object stores its data in regular properties (like `name`, `age` etc) and has functions to express itself. Function properties are usually called *methods*. So, one can say that in the code above "`sayHi` is a method of the object `user`". + +Of course we could use a Function Declaration for the same purpose: + +```js run +let user = { + // ... +}; + +*!* +function sayHi() { + alert("Hello!"); +}; + +user.sayHi = sayHi; +*/!* + +user.sayHi(); // Hello! +``` + +That would also work, but is longer. Also we get an "extra" function `sayHi` outside of the `user` object. Here we don't want it. + +```smart header="Object-oriented programming" +When we write our code using objects to represent entities, that's called an [object-oriented programming](https://en.wikipedia.org/wiki/Object-oriented_programming), in short: "OOP". + +As of now, we already know how to create an object with `{...}` and how to store data and add a method to it. But we will study it in detail later when we get enough familarity with basic functions of the language. +``` + + +## Function Expression vs Function Declaration + +Let's formulate the key differences between Function Declarations and Expressions. + +Here's the syntax distinction between these two. + +- *Function Declaration:* a function, declared as a separate statement, in the main code flow. + + ```js + // Function Declaration + function sum(a, b) { + return a + b; + } + ``` +- *Function Expression:* a function, created in the context of an expression. + + Here the function is created in the context of an "assignment expression =": + ```js + // Function Expression + let sum = function(a, b) { + return a + b; + } + ``` + +Another difference is when they are actualy created by the JavaScript engine. + +**Function Expressions are created when the execution reaches them and are usable since then.** + +That's kind of obvious. Once the execution flow passes to the right side of the assignment `let sum = function` -- here we go, the function is created and can be used (assigned, called etc) from now on. + +Function Declarations are different. + +**Function Declarations are usable in the whole script/code block.** + +In other words, when JavaScript *prepares* to run the script/code block, it first looks for Function Declarations in it and creates the functions. We can think of it as an "initialization stage". + +And after all Function Declarations are processed, it actually executes it. + +As a natural effect, a function declared as Function Declaration can be called earlier than it is defined. + +For example, this works: + +```js run refresh untrusted +*!* +sayHi("John"); // Hello, John +*/!* + +function sayHi(name) { + alert( `Hello, ${name}` ); +} +``` + +Function Declaration `sayHi` is created when JavaScript is preparing to start the script and is visible everywhere in it. + +...And if there were Function Expression, then it wouldn't work: + +```js run refresh untrusted +*!* +sayHi("John"); // error! +*/!* + +let sayHi = function(name) { // (*) no magic any more + alert( `Hello, ${name}` ); +}; +``` + +Function Expressions are created when the execution reaches them. That would happen only in the line `(*)`. Too late. + + +### Function Declaration in a block + +When Function Declaration is made within a code block, it is visible everywhere inside that block. But not outside of it. + +Sometimes that's handy to declare a local function, only needed in that only block. But can also be a problem. + +For instance, let's imagine that we need to declare a function `welcome()` depending on the `age` variable that we get in run time. And then use it sometimes later. + +The code below doesn't work: + +```js run +let age = prompt("What is your age?", 18); + +if (age < 18) { + + function welcome() { + alert("Hello!"); + } + +} else { + + function welcome() { + alert("Greetings!"); + } + +} + +*!* +welcome(); // Error: welcome is not defined +*/!* +``` + +The Function Declaration is visible only inside the code block where it resides. + +We can call it from within the block, but not from outside: + +```js run +let age = 16; // take 16 as an example + +if (age < 18) { +*!* + welcome(); // \ (runs) +*/!* + // | + function welcome() { // | + alert("Hello!"); // | Function Declaration is available + } // | everywhere in the block when it's declared + // | +*!* + welcome(); // / (runs) +*/!* + +} else { + // \ + function welcome() { // | + alert("Greetings!"); // | in this if test we don't enter this block, + } // | so this "welcome" is never created + // / +} + +// Now we're out of figure brackets, +// so we can not see Function Declarations made inside of them. + +*!* +welcome(); // Error: welcome is not defined +*/!* +``` + +What can we do to make `welcome` visible outside? + +The right thing would be to use a Function Expression and assign `welcome` to the variable which is declared outside of `if` and has the proper visibility: + +```js run +let age = prompt("What is your age?", 18); + +let welcome; + +if (age < 18) { + + welcome = function() { + alert("Hello!"); + } + +} else { + + welcome = function() { + alert("Greetings!"); + } + +} + +*!* +welcome(); // ok now +*/!* +``` + +Or we could go on to simplify it even further using a question mark operator `?`: + +```js run +let age = prompt("What is your age?", 18); + +let welcome = (age < 18) ? + function() { alert("Hello!"); } : function() { alert("Greetings!"); } + +*!* +welcome(); // ok now +*/!* +``` + + +```smart header="What to choose: a Declaration or an Expression?" +As a rule of thumb, a Function Declaration is prefered. It gives more freedom in how to organize our code, because we can call it both above and below. + +It's also a little bit easier to look up Function Declarations in the code, they increase readability of the code. + +But if a Function Declaration does not fit for some reason (we've seen an example), then a Function Expression should be used. +``` + + +## Named Function Expression + +Now the feature that exists for a Function Expression, but not for Function Declaration, and hence may be a reason to use them. + +**We can specify an "internal" name for a Function Expression.** + +Compare these two variants: + +```js +let sayHi = function() { // (1) + alert('Hello'); +}; + +let sayHi = function *!*func*/!*() { // (2) + alert('Hello'); +}; +``` + +Both create a function and put it into the variable `sayHi`. And usually we don't specify anything in the `function()`, so the 1st variant works fine. + +Although the function can move to another variable. As we've seen it's easy to copy a function and maybe replace the previous value with something else: + +```js run +let sayHi = function() { + alert('Hello'); +}; + +// oh maybe that's better? +let oldSayHi = sayHi; // keep the old variant here +sayHi = function() { // replace with a newer one + alert("What's up dude?"); +}; + + +oldSayHi(); // Hello +sayHi(); // What's up dude? +``` + +The problem may occur if a function references *itself* from inside. It happens when the function wants to access its properties (`sayHi.counter` in the example above), or sometimes it wants to call itself one more time. + + +But if the function has moved, then the old name is not relevant any move! + + +Let's see: + +```js run +// create a function +let sayHi = function() { + sayHi.counter++; + alert('Hi ' + sayHi.counter); +}; +sayHi.counter = 0; + +// move it +let movedSayHi = sayHi; + +// overwrite the old name to make things more obvious +sayHi = null; + +*!* +movedSayHi(); // Error: Cannot read property 'counter' of null +*/!* +``` + +The optional `name` which we can put into `function name()` is exactly meant to solve this kind of problems. + +- It is only visible from inside the function. +- It always references the current function. + +Let's use it to fix the code: + +```js run +// now with the internal name "say" +*!* +let sayHi = function say() { + say.counter++; + alert('Hi ' + say.counter); // and use it everywhere inside +}; +*/!* +sayHi.counter = 0; + +let movedSayHi = sayHi; + +sayHi = null; + +movedSayHi(); // Hi 1 +movedSayHi(); // Hi 2 (works) + +alert(say); // Error (say is undefined, that's normal) +``` + +Please note that: + +- The name `say` references the current function no matter where it is. That's why it works. +- The name `say` exists only inside the function. The last line demonstrates that. + +So the outer code still uses `sayHi` (or `movedSayHi` later). It doesn't see `say`, actually it doesn't need to see it. The `say` is an "internal function name", how it calls itself privately. + +A Function Expression with a name is called *Named Function Expression*. + +The "internal name" feature described here is only available for Function Expressions, not to Function Declarations. For Function Declarations, the `function name()` behaves like `sayHi`, there's just no syntax possibility to add a one more "internal" name for them. + + + +## Arrow functions [#arrow-functions] + +Enough with the complexities for now. Let's relax with another syntax of functions that can make our code shorter. + +Arrow functions act like a function expression, but look a little bit differently. + +The syntax is: + +```js +let func = (arg1, arg2, ...argN) => expression +``` + +...This creates a function `func` that has arguments `arg1..argN`, evaludates the `expression` on the right side with their use and returns its result. + +In other words, it's roughly the same as: + +```js +let func = function(arg1, arg2, ...argN) { + return expression; +} +``` + +Let's see the example: + +```js run +let sum = (a, b) => a + b; + +alert( sum(1, 2) ); // 3 +``` + +Here the function is same as: + +```js +let sum = function(a, b) { + return a + b; +} +``` + +If we have only one argument, then brackets can be omitted, making that even shorter: + +```js run +// same as +// let double = function(n) { return n*2 } +let double = n => n*2; + +alert( double(3) ); // 6 +``` + +If there are no arguments, we can put empty brackets. For instance, here's the rewritten example with `welcome()`: + +```js run +let age = prompt("What is your age?", 18); + +let welcome = (age < 18) ? () => alert('Hello') : () => alert("Greetings!"); + +welcome(); // ok now +``` + +The syntax may appear unfamiliar and not very readable at first, but that quickly changes as the eyes get used to the structure. + +Arrow functions are very convenient for simple one-line actions, when we're just lazy to write many words. + +```smart header="Multiline arrow functions" + +The examples above took arguments from the left of `=>` and evaluate the right-side expression with them. + +Sometimes we need something a little bit more complex, like multiple expressions or statements. It is also possible, but we should enclose them in figure brackets. Then use a normal `return` within them. + +Like this: + +```js run +let sum = (a, b) => { // the figure bracket opens a multiline function + let result = a + b; +*!* + return result; // if we use figure brackets, must use return +*/!* +} + +alert( sum(1, 2) ); // 3 +``` + +```smart header="More to come" +Here we praised arrow functions for shortness. But that's not all! Arrow functions have other interesting features in them. We'll return to them later and see where else they shine. + +As for now, we can already use them for one-line actions. +``` + +## new Function + +And the last syntax for the functions: +```js +let func = new Function('a, b', 'return a + b'); +``` + +The major difference is that it creates a function literally from a string, at run time. See, both arguments are strings. The first one lists the arguments, while the second one is the function body. + +All previous declarations required us, programmers, to write the function code in the script. + +But `new Function` allows to turn any string into a function, for example we can receive a new function from the server and then execute it: + +```js +let str = ... receive the code from the server dynamically ... + +let func = new Function('', str); +func(); +``` + +It is used in very specific cases, like when we receive the code from the server, or to dynamically compile a function from a template. The need for such uses arises at advanced stages of the development. + + +## Summary + +- Functions are values. They can be assigned, copied or declared in any place of the code. +- If the function is declared as a separate statement, in the main code flow -- that's called a Function Declaration. +- If the function is created as a part of an expression -- it's a Function Expression. +- Function Declarations are processed before the code block is executed. They are visible everywhere in the block (or the script). +- Function Expressions are created when the execution flow reaches them. +- Function Expressions allow to specify an optional name for internal needs (Named Function Expression). + + +If we simple want to create a function, then in most cases Function Declaration is preferable. + +Novice programmers sometimes tend to overuse Function Expression by creating many functions with `let func = function()`, but compare, which code is more readable: + +```js no-beautify +let f = function() { /* expression */ } + +function f() { /* declaration */ } +``` + +Function Declaration is shorter and more obvious. The additional bonus -- it can be called before the actual declaration. + +**Use Function Expression only when Function Declaration does not fit the task.** + +We've seen a couple of examples of that in the chapter. And will see more in the future. + +We also touched two other ways to create a function: + +- Arrow functions are handy for one-liners. The come in two flavours: + 1. Without figure brackets: `(...args) => expression` -- returns the evaluated `expression`. + 2. With brackets: `(...args) => { body }` -- need an explicit `return` statement to return something, but can be more complex. + +- `new Function(args, body)` + + This syntax allows to create a function from a string, that may be composed dynamically during the execution. diff --git a/1-js/3-code-quality/index.md b/1-js/3-code-quality/index.md index 3edc6943..6ab1130a 100644 --- a/1-js/3-code-quality/index.md +++ b/1-js/3-code-quality/index.md @@ -1,8 +1,4 @@ # TODO: Code quality -In order to keep the code quality, you need at least: -
    -
  1. The ability to debug code and fix bugs.
  2. -
  3. Good style code.
  4. -
  5. Test code is desirable - in automatic mode.
  6. -
+In this chapter we cover common, but very important things. Probably you'll want to reread some chapters later when you learn more about the language and write more code. + diff --git a/1-js/4-data-structures/11-date/article.md b/1-js/4-data-structures/11-date/article.md index 2846e261..c5519678 100644 --- a/1-js/4-data-structures/11-date/article.md +++ b/1-js/4-data-structures/11-date/article.md @@ -2,21 +2,21 @@ JavaScript has a built-in object [Date](mdn:js/Date) for date/time management. -It contains the date, the time, the timezone, everything. +It contains the date, time, timezone -- everything. [cut] ## Creation -To create a new `Date` object use one of the following syntaxes: +To create a new `Date` object call `new Date()` with one of the following arguments: `new Date()` -: Create a `Date` object for the current date and time: +: Without arguments -- create a `Date` object for the current date and time: ```js run let now = new Date(); - alert( now ); // current date/time + alert( now ); // shows current date/time ``` `new Date(milliseconds)` @@ -32,10 +32,18 @@ To create a new `Date` object use one of the following syntaxes: alert( Jan02_1970 ); ``` - The number of milliseconds is called a *timestamp*. It is a lightweight numeric representation of a date. We can always create a date from the timestamp number using `new Date(timestamp)` and get the timestamp from the existing `Date` object using `date.getTime()` (see below). + The number of milliseconds that has passed since the beginning of 1970 is called a *timestamp*. + + It is a lightweight numeric representation of a date. We can always create a date from a timestamp using `new Date(timestamp)` and convert the existing `Date` object to a timestamp, there's a `date.getTime()` method (see below). `new Date(datestring)` -: If there is a single argument -- a string, then it is parsed with the `Date.parse` algorithm (see below). +: If there is a single argument and it's a string, then it is parsed with the `Date.parse` algorithm (see below). + + + ```js run + let date = new Date("2017-01-26"); + alert( date ); // Thu Jan 26 2017 ... + ``` `new Date(year, month, date, hours, minutes, seconds, ms)` : Create the date with the given components in the local time zone. Only two first arguments are obligatory. @@ -147,7 +155,7 @@ alert( today ); // today, 00:00:00 sharp. ## Autocorrection -The *autocorrection* -- is a very handy property of the `Date` objects. We can set out-of-range values, and it will auto-adjust itself. +The *autocorrection* is a very handy feature of `Date` objects. We can set out-of-range values, and it will auto-adjust itself. For instance: @@ -156,7 +164,7 @@ let date = new Date(2013, 0, *!*32*/!*); // 32 Jan 2013 ?!? alert(date); // ...is 1st Feb 2013! ``` -**Out-of-range date components are distributed around automatically.** +**Out-of-range date components are distributed automatically.** Let's say we need to increase the date "28 Feb 2016" by 2 days. It may be "2 Mar" or "1 Mar" in case of a leap-year. We don't need to think about it. Just add 2 days. The `Date` object will do the rest: diff --git a/1-js/4-data-structures/4-object/article.md b/1-js/4-data-structures/4-object/article.md index ed3eeae9..1094d879 100644 --- a/1-js/4-data-structures/4-object/article.md +++ b/1-js/4-data-structures/4-object/article.md @@ -18,16 +18,16 @@ We can imagine it as a cabinet with signed files. Every piece of data is stored ## Object literals -An empty object ("empty cabinet") can be created using one of to syntaxes: +An empty object ("empty cabinet") can be created using one of two syntaxes: ```js -let user = new Object(); // works the same as below -let user = {}; +let user = new Object(); // "object constructor" syntax +let user = {}; // "object literal" syntax ``` ![](object-user-empty.png) -Usually, the figure brackets `{...}` syntax is used, because it's shorter. It is called an *object literal*. +Usually, the figure brackets `{...}` are used, they are more powerful shorter. The declaration is called an *object literal*. We can set properties immediately: @@ -108,8 +108,19 @@ let obj = { } 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" @@ -392,6 +403,7 @@ 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. + ## Cloning objects What if we need to duplicate an object? Create an independant copy, a clone? diff --git a/1-js/4-data-structures/7-array/article.md b/1-js/4-data-structures/7-array/article.md index a5568538..d1bbc116 100644 --- a/1-js/4-data-structures/7-array/article.md +++ b/1-js/4-data-structures/7-array/article.md @@ -76,6 +76,21 @@ alert( arr[1].name ); // John arr[3](); // hello ``` + +````smart header="Trailing comma" +An array may end with a comma: +```js +let fruits = [ + "Apple", + "Orange", + "Plum"*!*,*/!* +]; +``` + +The "trailing comma" style makes it easier to insert/remove items, because all lines become alike. +```` + + ## Methods pop/push, shift/unshift A [queue](https://en.wikipedia.org/wiki/Queue_(abstract_data_type)) is one of most common uses of an array. In computer science, this means an ordered collection of elements which supports two operations: @@ -292,21 +307,12 @@ But that's actually a bad idea. There are potential problems with it: 1. The loop `for..in` iterates over *all properties*, not only the numeric ones. - In the browser as well as in other environments, there are many collections of elements that *look like arrays*. That is, they have `length` and indexes properties, but they have *other non-numeric properties too*, which we usually don't need. The `for..in` loop will list them. If we need to work with arrays and those array-like structures, then these "extra" properties can become a problem. + There are so-called "array-like" objects in the browser and in other environments, that *look like arrays*. That is, they have `length` and indexes properties, and can be used in `for..of`, but they have *other non-numeric properties and methods*, which we usually don't need in the loop. The `for..in` will list them. If we need to work with array-like objects, then these "extra" properties can become a problem. 2. The `for..in` loop is optimized for generic objects, not arrays, and thus is 10-100 times slower. So we should never use `for..in` for arrays. -```smart header="Iterable objects" -Javascript has a generic concept of *iterables* or, in other words, "array-like" objects. - -An array-like object can have methods and properties of its own, but also implement special methods to be useable in `for..of` loop. We'll often meet such objects and meanwhile will learn to implement iterables by ourselves. - -TODO ??????????????????HERE ????????????? -``` - - ## A word about "length" The `length` property automatically updates when we modify the array. It is actually not the *count* of values in the array, but the greatest numeric index plus one. diff --git a/1-js/4-data-structures/index.md b/1-js/4-data-structures/index.md index 774dee46..a548c1ec 100644 --- a/1-js/4-data-structures/index.md +++ b/1-js/4-data-structures/index.md @@ -1,3 +1,3 @@ -# Структуры данных +# Data structures -Изучаем JavaScript: расширенное знакомство со встроенными типами данных, их особенностями. \ No newline at end of file +More data structures and more in-depth study of the known ones.