This commit is contained in:
Ilya Kantor 2015-11-27 11:20:02 +03:00
parent 6fd7a2f7f3
commit 864fcc93e2
3 changed files with 60 additions and 175 deletions

View file

@ -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

View file

@ -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>

View file

@ -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]