ok
This commit is contained in:
parent
6fd7a2f7f3
commit
864fcc93e2
3 changed files with 60 additions and 175 deletions
|
@ -1,111 +0,0 @@
|
||||||
|
|
||||||
# Let, const and var revisited
|
|
||||||
|
|
||||||
**KILL ME**
|
|
||||||
|
|
||||||
Now as we know most language syntax constructs, let's discuss the subtle features and differences between variable definitions: `let`, `var` and `const`.
|
|
||||||
|
|
||||||
## Let
|
|
||||||
|
|
||||||
We'll start with `let`, because in the modern code this declaration appears far more often than any other.
|
|
||||||
|
|
||||||
### Let is local to the block
|
|
||||||
|
|
||||||
A variable declared with `let` is local to the containing `{…}`.
|
|
||||||
|
|
||||||
In other words, the variable is only visible inside the block `{…}` where it was declared.
|
|
||||||
|
|
||||||
For instance, let's consider the following code:
|
|
||||||
|
|
||||||
```js
|
|
||||||
//+ run
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
let user = "John";
|
|
||||||
|
|
||||||
alert(`Hello ${user}!`); // Hello John!
|
|
||||||
|
|
||||||
if (true) {
|
|
||||||
*!*
|
|
||||||
let user = "Pete";
|
|
||||||
*/!*
|
|
||||||
alert(`…working with ${user}`); // …working with Pete
|
|
||||||
}
|
|
||||||
|
|
||||||
alert(`Goodbye ${user}!`); // Goodbye John!
|
|
||||||
```
|
|
||||||
|
|
||||||
Here the if-block declares and uses it's own variable named `"user"`. The code inside that `if` will see and use the local variable ("Pete"). And after the `if` finished, the if-local `user` is no longer seen ("John" again).
|
|
||||||
|
|
||||||
Let's see what happens if we remove the `let` inside `if`:
|
|
||||||
|
|
||||||
```js
|
|
||||||
//+ run
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
let user = "John";
|
|
||||||
|
|
||||||
alert(`Hello ${user}!`); // Hello John!
|
|
||||||
|
|
||||||
if (true) {
|
|
||||||
*!*
|
|
||||||
user = "Pete";
|
|
||||||
*/!*
|
|
||||||
alert(`…working with ${user}`); // …working with Pete
|
|
||||||
}
|
|
||||||
|
|
||||||
alert(`Goodbye ${user}!`); // Goodbye *!*Pete*/!*!
|
|
||||||
```
|
|
||||||
|
|
||||||
Now there is no declaration inside `if`, there's no local `user`, hence the outer (the only declared) variable is used and modified.
|
|
||||||
|
|
||||||
The same applies to other `{…}` blocks, including `for`, `while`, `switch` and other constructs.
|
|
||||||
|
|
||||||
[smart header="`let` in `for` is also local"]
|
|
||||||
In `for(let i=…)` the variable declared inside `for` is local to the loop body.
|
|
||||||
|
|
||||||
For instance, `i` in the example below is visible only inside the loop:
|
|
||||||
|
|
||||||
```js
|
|
||||||
//+ run
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
for(let i = 0; i < 3; i++) {
|
|
||||||
// ...i becomes: 0, 1, 2
|
|
||||||
alert(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// i no longer exists here
|
|
||||||
alert(i); // Error: i is not defined!
|
|
||||||
```
|
|
||||||
[/smart]
|
|
||||||
|
|
||||||
### Let is visible only after the declaration
|
|
||||||
|
|
||||||
The variable can only be used after it is declared:
|
|
||||||
|
|
||||||
```js
|
|
||||||
//+ run
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
alert(message); // Error, the variable does not exist yet!
|
|
||||||
|
|
||||||
let message = "hello";
|
|
||||||
```
|
|
||||||
|
|
||||||
In the code above we must put `alert` after `let message` for it to work.
|
|
||||||
|
|
||||||
That seems to be obvious. But a little later we'll see that `var` behaves differently here.
|
|
||||||
|
|
||||||
## Const
|
|
||||||
|
|
||||||
A variable declared with `const` is exactly the same as `let` except that it cannot be modified.
|
|
||||||
|
|
||||||
The visibility rules are same.
|
|
||||||
|
|
||||||
## Var
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -83,7 +83,7 @@ function showMessage() {
|
||||||
showMessage(); // Hello, my name is John
|
showMessage(); // Hello, my name is John
|
||||||
```
|
```
|
||||||
|
|
||||||
The function can not only read but also modify an outer variable.
|
The function has a full access to an outer variable. It can modify it as well.
|
||||||
|
|
||||||
For instance:
|
For instance:
|
||||||
|
|
||||||
|
@ -107,7 +107,11 @@ alert( userName ); // Bob, the value was modified by the function
|
||||||
*/!*
|
*/!*
|
||||||
```
|
```
|
||||||
|
|
||||||
If we had `let` before `userName` in the line (1) then the function would have a local variable `userName` and use it instead of the outer one:
|
Let's note that an outer variable is only used if there's no local with the same name.
|
||||||
|
|
||||||
|
For example, if we had `let` before `userName` in the line (1) then the function would have a local variable `userName` and use it instead of the outer one. This process is called "shadowing".
|
||||||
|
|
||||||
|
In the code below the local `userName` shadows the outer one:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
//+ run
|
//+ run
|
||||||
|
@ -126,69 +130,48 @@ function showMessage() {
|
||||||
showMessage();
|
showMessage();
|
||||||
|
|
||||||
*!*
|
*!*
|
||||||
alert( userName ); // John, unmodified
|
alert( userName ); // John, the function did not access the outer variable
|
||||||
*/!*
|
*/!*
|
||||||
```
|
```
|
||||||
|
|
||||||
## Global variables
|
[smart header="Global variables"]
|
||||||
|
Variables declared outside of any function, are called *global*.
|
||||||
|
|
||||||
Variables declared on the script level, outside of any function, are called *global*.
|
Global variables are visible from any function.
|
||||||
|
|
||||||
Global variables are visible from any function. They are used to store the data of a project-wide importance. Variables needed by specific tasks should reside in the corresponding functions.
|
They should be only used to store the data of project-wide importance. Variables needed by specific tasks should reside in the corresponding functions.
|
||||||
|
[/smart]
|
||||||
Without strict mode, for compatibility with the old scripts, it is possible to create a variable by an assignment, without a declaration.
|
|
||||||
|
|
||||||
Consider the code below as an example. The variable `message` becomes global.
|
|
||||||
|
|
||||||
```js
|
|
||||||
//+ run no-strict
|
|
||||||
function showMessage() {
|
|
||||||
message = 'Hello'; // (*) assignment without declaration!
|
|
||||||
}
|
|
||||||
|
|
||||||
showMessage();
|
|
||||||
|
|
||||||
alert( message ); // Hello
|
|
||||||
```
|
|
||||||
|
|
||||||
With `"use strict"` there will be an error in line `(*)`. Without it, there won't be.
|
|
||||||
|
|
||||||
In the code above, most probably, the programmer simply forgot to write `let`, so the error message is a good thing. A one more reason to `"use strict"` all the time.
|
|
||||||
|
|
||||||
Modern editors and tools for code quality checking like [jshint](http://jshint.com/) allow to see and fix "missed declarations" early while coding.
|
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
We can pass arbitrary data to the function using it's parameters (also called *arguments*) .
|
We can pass arbitrary data to the function using it's parameters (also called *function arguments*) .
|
||||||
|
|
||||||
For example, this code shows two messages:
|
In the example below, the function has two parameters: `from` and `text`.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
//+ run no-beautify
|
//+ run no-beautify
|
||||||
function showMessage(*!*from, text*/!*) { // arguments: from, text
|
function showMessage(*!*from, text*/!*) { // arguments: from, text
|
||||||
|
|
||||||
from = "**" + from + "**";
|
from = "[" + from + "]";
|
||||||
|
|
||||||
alert(from + ': ' + text);
|
alert(from + ': ' + text);
|
||||||
}
|
}
|
||||||
|
|
||||||
*!*
|
*!*
|
||||||
showMessage('Ann', 'Hello!'); // **Ann**: Hello!
|
showMessage('Ann', 'Hello!'); // [Ann]: Hello!
|
||||||
showMessage('Ann', "What's up?"); // **Ann**: What's up?
|
showMessage('Ann', "What's up?"); // [Ann]: What's up?
|
||||||
*/!*
|
*/!*
|
||||||
```
|
```
|
||||||
|
|
||||||
In the example above, the function has two parameters: `from` and `text`.
|
When the function is called, the values in the brackets are copied to local variables `from` and `next`. The function can modify them.
|
||||||
|
|
||||||
When the function is called, the values in the brackets are copied to local variables `from` and `next`.
|
Note that the changes are not seen from outside:
|
||||||
|
|
||||||
Note that the function can modify those local variables freely, they are not seen from outside anyway:
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
//+ run
|
//+ run
|
||||||
function showMessage(from, text) {
|
function showMessage(from, text) {
|
||||||
*!*
|
*!*
|
||||||
from = '**' + from + '**'; // changes the local from
|
from = '[' + from + ']'; // changes the local from
|
||||||
*/!*
|
*/!*
|
||||||
alert( from + ': ' + text );
|
alert( from + ': ' + text );
|
||||||
}
|
}
|
||||||
|
@ -203,15 +186,15 @@ alert( from ); // Ann
|
||||||
|
|
||||||
## Default parameter values
|
## Default parameter values
|
||||||
|
|
||||||
A function can be *called* with any number of arguments. If a parameter is not provided, but listed in the declaration, then its value becomes `undefined`.
|
A function can be called with any number of arguments. If a parameter is not provided, but listed in the declaration, then its value becomes `undefined`.
|
||||||
|
|
||||||
For instance, a function `showMessage(from, text)` can actually be called with a single argument:
|
For instance, the aforementioned function `showMessage(from, text)` can be called with a single argument:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
showMessage("Ann");
|
showMessage("Ann");
|
||||||
```
|
```
|
||||||
|
|
||||||
Normally, such call would output `"Ann: undefined"`, because `text === undefined`.
|
That's not an error. Such call would output `"Ann: undefined"`, because `text === undefined`.
|
||||||
|
|
||||||
But we can modify the function to detect missed parameter and assign a "default value" to it:
|
But we can modify the function to detect missed parameter and assign a "default value" to it:
|
||||||
|
|
||||||
|
@ -250,7 +233,7 @@ function showMessage(from, text) {
|
||||||
|
|
||||||
This way is shorter, but the argument is considered missing also if it's falsy, like an empty line, `0` or `null`.
|
This way is shorter, but the argument is considered missing also if it's falsy, like an empty line, `0` or `null`.
|
||||||
</li>
|
</li>
|
||||||
<li>ES-2015 introduced an neater syntax for default values:
|
<li>ES-2015 introduced a neater syntax for default values:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
//+ run
|
//+ run
|
||||||
|
@ -262,8 +245,6 @@ showMessage("Ann"); // Ann: no text for you
|
||||||
```
|
```
|
||||||
|
|
||||||
Here `'no text given'` is a string, but it can be any other value or expression, which is only evaluated and assigned if the parameter is missing.
|
Here `'no text given'` is a string, but it can be any other value or expression, which is only evaluated and assigned if the parameter is missing.
|
||||||
|
|
||||||
This syntax is not yet widely supported in the browsers, but available with the help of transpilers like Babel.
|
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
|
@ -271,22 +252,21 @@ This syntax is not yet widely supported in the browsers, but available with the
|
||||||
|
|
||||||
A function can return a value into the calling code as the result.
|
A function can return a value into the calling code as the result.
|
||||||
|
|
||||||
For instance, let's make a mathematical function `calcD` to calculate a [discriminant of a quadratic polynomial](https://en.wikipedia.org/wiki/Discriminant#Quadratic_formula) using the formula <code>b<sup>2</sup> - 4ac</code>:
|
The simplest example would be a function that sums two values:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
//+ run no-beautify
|
//+ run no-beautify
|
||||||
function calcD(a, b, c) {
|
function sum(a, b) {
|
||||||
*!*return*/!* b*b - 4*a*c;
|
*!*return*/!* a + b;
|
||||||
}
|
}
|
||||||
|
|
||||||
// discriminant for: -4x^2 + 2x + 1
|
let result = sum(1, 2);
|
||||||
let test = calcD(-4, 2, 1);
|
alert( result ); // 3
|
||||||
alert(test); // 20
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The directive `return` can be in any place of the function. When the execution reaches it, the function stops, and the value is returned to the calling code (assigned to `test` above).
|
The directive `return` can be in any place of the function. When the execution reaches it, the function stops, and the value is returned to the calling code (assigned to `result` above).
|
||||||
|
|
||||||
There may be many returns, for instance:
|
There may be many uses of `return` in a function. For instance:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
//+ run
|
//+ run
|
||||||
|
@ -307,7 +287,7 @@ if ( checkAge(age) ) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The `return` can be used without a value, to exit from the function immediately.
|
It is possible to use `return` without a value. That causes the function to exit immediately.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
|
@ -327,7 +307,7 @@ function showMovie(age) {
|
||||||
In the code above, if `checkAge(age)` returns `false`, then `showMovie` won't proceed to the `alert`.
|
In the code above, if `checkAge(age)` returns `false`, then `showMovie` won't proceed to the `alert`.
|
||||||
|
|
||||||
|
|
||||||
[smart header="A result with an empty or absent `return` returns `undefined`"]
|
[smart header="A function with an empty `return` or without it returns `undefined`"]
|
||||||
If a function does not return a value, it is the same as if it returns `undefined`:
|
If a function does not return a value, it is the same as if it returns `undefined`:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
@ -337,7 +317,7 @@ function doNothing() { /* empty */ }
|
||||||
alert( doNothing() ); // undefined
|
alert( doNothing() ); // undefined
|
||||||
```
|
```
|
||||||
|
|
||||||
A `return` with no argument is also the same as `return undefined`:
|
An empty `return` is also the same as `return undefined`:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
//+ run
|
//+ run
|
||||||
|
@ -376,24 +356,23 @@ Examples of such names:
|
||||||
```js
|
```js
|
||||||
//+ no-beautify
|
//+ no-beautify
|
||||||
getAge(..) // return the age (get it somehow)
|
getAge(..) // return the age (get it somehow)
|
||||||
calcD(..) // calculate a discriminant and return the result
|
calcSum(..) // calculate a sum and return the result
|
||||||
createForm(..) // create a form, usually returns it
|
createForm(..) // create a form, usually returns it
|
||||||
checkPermission(..) // check a permission, return true/false
|
checkPermission(..) // check a permission, return true/false
|
||||||
```
|
```
|
||||||
|
|
||||||
That's very convenient. The prefix itself is a great hint. A glance on a function name gives an understanding what it does and what kind of value it returns.
|
The agreement about prefixes is very convenient. A glance on a function name, even on the prefix of it, gives an understanding what it does and what kind of value it returns.
|
||||||
|
|
||||||
[smart header="One function -- one action"]
|
[smart header="One function -- one action"]
|
||||||
A function should do exactly what is suggested by its name.
|
A function should do exactly what is suggested by its name.
|
||||||
|
|
||||||
Two independant actions usually deserve two functions, even if they are usually called together (in that case we can make a 3rd function calling those two).
|
Two independant actions usually deserve two functions, even if they are usually called together (in that case we can make a 3rd function calling those two).
|
||||||
|
|
||||||
Few examples of "shouldn't do" for the names listed above:
|
Few examples of breaking this rule:
|
||||||
<ul>
|
<ul>
|
||||||
<li>`getAge` -- should not change `age`, also it should not show anything to the visitor.</li>
|
<li>`getAge` -- if shows the age to the visitor (should only get).</li>
|
||||||
<li>`calcD` -- should not store a calculated discriminant "for future reuse" anywhere. It should only calculate and return it.</li>
|
<li>`createForm` -- if modifies the document, adds a form to it (should only create it and return).</li>
|
||||||
<li>`createForm` -- should not show the form to the user or modify something in the document. It should only create it and return.</li>
|
<li>`checkPermission` -- if displays the `access granted/denied` message or stores the result of the check anywhere (should only perform the check and return the result).</li>
|
||||||
<li>`checkPermission` -- should only perform the check and return the result. It should not display the `access granted/denied` message.</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
[/smart]
|
[/smart]
|
||||||
|
|
||||||
|
@ -402,6 +381,8 @@ Few examples of "shouldn't do" for the names listed above:
|
||||||
Functions that are used *very often* sometimes have ultrashort names.
|
Functions that are used *very often* sometimes have ultrashort names.
|
||||||
|
|
||||||
For example, [jQuery](http://jquery.com) framework defines a function `$`, [LoDash](http://lodash.com/) library has it's core function named `_`.
|
For example, [jQuery](http://jquery.com) framework defines a function `$`, [LoDash](http://lodash.com/) library has it's core function named `_`.
|
||||||
|
|
||||||
|
These are exceptions though. Generally the functions names should be concise, but descriptive.
|
||||||
[/smart]
|
[/smart]
|
||||||
|
|
||||||
|
|
||||||
|
@ -416,8 +397,8 @@ function name(parameters, delimited, by, comma) {
|
||||||
```
|
```
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>Values passed in a function call become its local variables.</li>
|
<li>Values passed to function as parameters are copied to its local variables.</li>
|
||||||
<li>A function can declare local variables.</li>
|
<li>A function may access outer variables. But it works only one-way. The code outside of the function doesn't see its local variables.</li>
|
||||||
<li>A function can return a value. If it doesn't then its result is `undefined`.</li>
|
<li>A function can return a value. If it doesn't then its result is `undefined`.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
|
|
@ -203,7 +203,6 @@ var userClone = copy({}, user);
|
||||||
А вот и реализация:
|
А вот и реализация:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
//+ autorun
|
|
||||||
function copy() {
|
function copy() {
|
||||||
var dst = arguments[0];
|
var dst = arguments[0];
|
||||||
|
|
||||||
|
@ -424,3 +423,19 @@ function showMessage(text, options) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
[head]
|
||||||
|
<script>
|
||||||
|
function copy() {
|
||||||
|
var dst = arguments[0];
|
||||||
|
|
||||||
|
for (var i = 1; i < arguments.length; i++) {
|
||||||
|
var arg = arguments[i];
|
||||||
|
for (var key in arg) {
|
||||||
|
dst[key] = arg[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
[/head]
|
Loading…
Add table
Add a link
Reference in a new issue