This commit is contained in:
Ilya Kantor 2015-10-02 23:43:08 +02:00
parent 1d236fddf8
commit 72528a31fd

View file

@ -130,12 +130,12 @@ alert( userName ); // John, the outer variable is not modified
*/!* */!*
``` ```
**Variables declared on the most outer level, not in any function, are called *global*.** **Variables declared on the script level, outside of any function, are called *global*.**
Please only declare global the variables which have a project-wide importance. Variables needed by specific tasks should reside in the corresponding functions. So to say, global variables are rare in modern projects. 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.
[warn header="Attention: implicit global declaration!"] [warn header="Attention: implicit global declaration!"]
Without strict mode, for compatibility with the old scripts, it is possible to omit variable declaration: Without strict mode, for compatibility with the old scripts, it is possible to create a variable by an assignment, without a declaration:
```js ```js
//+ run //+ run
@ -148,116 +148,132 @@ showMessage();
alert( message ); // Hello alert( message ); // Hello
``` ```
In the code above `message` was not declared. Most probably, the programmer simply forgot to write `let`. In the code above, most probably, the programmer simply forgot to write `let`. As a result, he created a global variable `message`.
With `"use strict"` there will be an error. But without it, the variable will be created automatically. It will be global. With `"use strict"` there will be an error.
Modern editors and tools for code quality checking like [jshint](http://jshint.com/) allow to see and fix "missed declarations" early while coding. Modern editors and tools for code quality checking like [jshint](http://jshint.com/) allow to see and fix "missed declarations" early while coding.
[/warn] [/warn]
Later, after we deal with the basics and data structures, in the chapter [](/closures) we will go deeper in the internals of functions and variables. Later, after we deal with the basics and data structures, in the chapter [](/closures) we will go deeper in the internals of functions and variables.
## Параметры ## Parameters
При вызове функции ей можно передать данные, которые та использует по своему усмотрению. We can pass arbitrary data to the function using it's parameters (also called *arguments*) .
Например, этот код выводит два сообщения: For example, this code shows two messages:
```js ```js
//+ run no-beautify //+ run no-beautify
function showMessage(*!*from, text*/!*) { // параметры from, text function showMessage(*!*from, text*/!*) { // arguments: from, text
from = "** " + from + " **"; // здесь может быть сложный код оформления from = "**" + from + "**";
alert(from + ': ' + text); alert(from + ': ' + text);
} }
*!* *!*
showMessage('Маша', 'Привет!'); showMessage('Ann', 'Hello!'); // **Ann**: Hello!
showMessage('Маша', 'Как дела?'); showMessage('Ann', "What's up?"); // **Ann**: What's up?
*/!* */!*
``` ```
**Параметры копируются в локальные переменные функции**. In the example above, the function has two parameters: `from` and `text`.
Например, в коде ниже есть внешняя переменная `from`, значение которой при запуске функции копируется в параметр функции с тем же именем. Далее функция работает уже с параметром: When the function is called, the values in the brackets are copied to local variables `from` and `next`.
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 + '**'; // меняем локальную переменную from from = '**' + from + '**'; // changes the local from
*/!* */!*
alert( from + ': ' + text ); alert( from + ': ' + text );
} }
let from = "Маша"; let from = "Ann";
showMessage(from, "Привет"); showMessage(from, "Hello");
alert( from ); // старое значение from без изменений, в функции была изменена копия // the old value of "from" is still here, the function modified a local copy
alert( from ); // Ann
``` ```
## Аргументы по умолчанию ## Default arguments
Функцию можно вызвать с любым количеством аргументов. 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`.
Если параметр не передан при вызове -- он считается равным `undefined`. For instance, a function `showMessage(from, text)` can actually be called with a single argument:
Например, функцию показа сообщения `showMessage(from, text)` можно вызвать с одним аргументом:
```js ```js
showMessage("Маша"); showMessage("Ann");
``` ```
При этом можно проверить, и если параметр не передан -- присвоить ему значение "по умолчанию": Normally, 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:
```js ```js
//+ run //+ run
function showMessage(from, text) { function showMessage(from, text) {
*!* *!*
if (text === undefined) { if (text === undefined) {
text = 'текст не передан'; text = 'no text given';
} }
*/!* */!*
alert( from + ": " + text ); alert( from + ": " + text );
} }
showMessage("Маша", "Привет!"); // Маша: Привет! showMessage("Ann", "Hello!"); // Ann: Hello!
*!* *!*
showMessage("Маша"); // Маша: текст не передан showMessage("Ann"); // Ann: no text given
*/!* */!*
``` ```
**При объявлении функции необязательные аргументы, как правило, располагают в конце списка.** Optional arguments are usually given at the end of the list.
Для указания значения "по умолчанию", то есть, такого, которое используется, если аргумент не указан, используется два способа: There are three most used ways to assign default values:
<ol> <ol>
<li>Можно проверить, равен ли аргумент `undefined`, и если да -- то записать в него значение по умолчанию. Этот способ продемонстрирован в примере выше.</li> <li>We can check if the argument equals `undefined`, and if yes then assign a default value to it. That's demonstrated by the example above.</li>
<li>Использовать оператор `||`: <li>Use operator `||`:
```js ```js
//+ run
function showMessage(from, text) { function showMessage(from, text) {
text = text || 'текст не передан'; text = text || 'no text given';
... ...
} }
``` ```
Второй способ считает, что аргумент отсутствует, если передана пустая строка, `0`, или вообще любое значение, которое в логическом контексте является `false`. This way is shorter, but the argument is considered missing also if it's falsy, like an empty line, `0` or `null`.
</li>
<li>ES-2015 introduces an neater syntax for default values:
```js
//+ run
function showMessage(from, *!*text = 'no text given'*/!*) {
alert( from + ": " + text );
}
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.
This syntax is not yet widely supported in the browsers, but available with the help of transpilers like Babel.
</li> </li>
</ol> </ol>
Если аргументов передано больше, чем надо, например `showMessage("Маша", "привет", 1, 2, 3)`, то ошибки не будет. Но, чтобы получить такие "лишние" аргументы, нужно будет прочитать их из специального объекта `arguments`, который мы рассмотрим в главе [](/arguments-pseudoarray). A call may also provide more arguments than listed, for instance: `showMessage("Ann", "Hello", 1, 2, 3)`. Later we'll see how to read such extra arguments.
## Возврат значения ## Returning a value
Функция может возвратить результат, который будет передан в вызвавший её код. A function can do return a value into the calling code.
Например, создадим функцию `calcD`, которая будет возвращать дискриминант квадратного уравнения по формуле <code>b<sup>2</sup> - 4ac</code>: For instance, let's make a maths function `calcD` to calculate a [discriminant of a quadratic polynomial] using the formula <code>b<sup>2</sup> - 4ac</code>:
```js ```js
//+ run no-beautify //+ run no-beautify
@ -265,15 +281,14 @@ function calcD(a, b, c) {
*!*return*/!* b*b - 4*a*c; *!*return*/!* b*b - 4*a*c;
} }
// discriminant for: -4x^2 + 2x + 1
let test = calcD(-4, 2, 1); let test = calcD(-4, 2, 1);
alert(test); // 20 alert(test); // 20
``` ```
**Для возврата значения используется директива `return`.** 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).
Она может находиться в любом месте функции. Как только до неё доходит управление -- функция завершается и значение передается обратно. There may be many returns, for instance:
Вызовов `return` может быть и несколько, например:
```js ```js
//+ run //+ run
@ -281,40 +296,41 @@ function checkAge(age) {
if (age > 18) { if (age > 18) {
return true; return true;
} else { } else {
return confirm('Родители разрешили?'); return confirm('Got a permission from the parents?');
} }
} }
let age = prompt('Ваш возраст?'); let age = prompt('How old are you?', 18);
if (checkAge(age)) { if ( checkAge(age) ) {
alert( 'Доступ разрешен' ); alert( 'Access granted' );
} else { } else {
alert( 'В доступе отказано' ); alert( 'Access denied' );
} }
``` ```
Директива `return` может также использоваться без значения, чтобы прекратить выполнение и выйти из функции. The `return` can be used without a value, to exit from the function immediately.
Например: For example:
```js ```js
function showMovie(age) { function showMovie(age) {
if (!checkAge(age)) { if ( !checkAge(age) ) {
*!* *!*
return; return;
*/!* */!*
} }
alert( "Фильм не для всех" ); // (*) alert( "Showing you the movie" ); // (*)
// ... // ...
} }
``` ```
В коде выше, если сработал `if`, то строка `(*)` и весь код под ней никогда не выполнится, так как `return` завершает выполнение функции. In the code above, if `checkAge(age)` returns `false`, then `showMovie` won't proceed to the `alert`.
[smart header="Значение функции без `return` и с пустым `return`"]
В случае, когда функция не вернула значение или `return` был без аргументов, считается что она вернула `undefined`: [smart header="A result with an empty or absent `return` returns `undefined`"]
If a function does not return a value, it is considered to return `undefined`:
```js ```js
//+ run //+ run
@ -323,9 +339,7 @@ function doNothing() { /* пусто */ }
alert( doNothing() ); // undefined alert( doNothing() ); // undefined
``` ```
Обратите внимание, никакой ошибки нет. Просто возвращается `undefined`. The same happens when the `return` has no argument:
Ещё пример, на этот раз с `return` без аргумента:
```js ```js
//+ run //+ run
@ -338,84 +352,93 @@ alert( doNothing() === undefined ); // true
[/smart] [/smart]
## Выбор имени функции [#function-naming] ## Naming a function [#function-naming]
Имя функции следует тем же правилам, что и имя переменной. Основное отличие -- оно должно быть глаголом, т.к. функция -- это действие. Functions are actions. So their name is usually a verb.
Как правило, используются глагольные префиксы, обозначающие общий характер действия, после которых следует уточнение. Usually, function names have verbal prefixes which vaguely describe the action.
Функции, которые начинаются с `"show"` -- что-то показывают: Functions that shart with `"show"` -- usually show something:
```js ```js
//+ no-beautify //+ no-beautify
showMessage(..) // префикс show, "показать" сообщение showMessage(..) // shows a message
``` ```
Функции, начинающиеся с `"get"` -- получают, и т.п.: <ul>
<li>Functions starting with `"get"` -- allow to get something,</li>
<li>with `"calc"` -- calculate something,</li>
<li>with `"create"` -- create something,</li>
<li>with `"check"` -- check something and return a boolean, etc.</li>
</ul>
```js ```js
//+ no-beautify //+ no-beautify
getAge(..) // get, "получает" возраст getAge(..) // returns the age (gets it somehow)
calcD(..) // calc, "вычисляет" дискриминант calcD(..) // calculate a discriminant and return the result
createForm(..) // create, "создает" форму createForm(..) // create a form, usually returns it
checkPermission(..) // check, "проверяет" разрешение, возвращает true/false checkPermission(..) // check a permission, return true/false
``` ```
Это очень удобно, поскольку взглянув на функцию -- мы уже примерно представляем, что она делает, даже если функцию написал совсем другой человек, а в отдельных случаях -- и какого вида значение она возвращает. Most prefixes are reused across multiple projects and understood by the community.
[smart header="Одна функция -- одно действие"] That's very convenient: a glance on a function name gives an understanding what it does and what kind of value it returns.
Функция должна делать только то, что явно подразумевается её названием. И это должно быть одно действие. [smart header="One function -- one action"]
A function should do exactly what is suggested by its name. If many subactions are involved and the code becomes large -- maybe it's worth to separate them in their functions.
Если оно сложное и подразумевает поддействия -- может быть имеет смысл выделить их в отдельные функции? Зачастую это имеет смысл, чтобы лучше структурировать код. By all means, two independant actions deserve two functions, even if they are related.
**...Но самое главное -- в функции не должно быть ничего, кроме самого действия и поддействий, неразрывно связанных с ним.** A few examples of "shouldn't" to see what I mean here:
<ul>
Например, функция проверки данных (скажем, `"validate"`) не должна показывать сообщение об ошибке. Её действие -- проверить. <li>`getAge` should not show update `age` or change it or show anything to the visitor.</li>
<li>`calcD` should not save a calculated discriminant "for future reuse". It should only calculate it.</li>
<li>`createForm` should not show the form to the user or modify something in the document. It should only create it.</li>
<li>`checkPermission` should only perform the check and return the result. Not display the `access granted/access denied` message.</li>
</ul>
[/smart] [/smart]
[smart header="Сверхкороткие имена функций"] [smart header="Ultrashort function names"]
Имена функций, которые используются *очень часто*, иногда делают сверхкороткими. Functions that are used *very often* sometimes have ultrashort names.
Например, во фреймворке [jQuery](http://jquery.com) есть функция `$`, во фреймворке [Prototype](http://prototypejs.com) -- функция `$$`, а в библиотеке [LoDash](http://lodash.com/) очень активно используется функция с названием из одного символа подчеркивания `_`. For example, [jQuery](http://jquery.com) framework defines a function `$`, [LoDash](http://lodash.com/) library has it's core function named `_`.
[/smart] [/smart]
## Итого
Объявление функции имеет вид: ## Summary
A function declaration looks like this:
```js ```js
function имя(параметры, через, запятую) { function name(parameters, delimited, by, comma) {
код функции /* code */
} }
``` ```
<ul> <ul>
<li>Передаваемые значения копируются в параметры функции и становятся локальными переменными.</li> <li>Values passed in a function call become its local variables.</li>
<li>Параметры функции копируются в её локальные переменные.</li> <li>A function can declare local variables.</li>
<li>Можно объявить новые локальные переменые при помощи `var`.</li> <li>A function can return a value. If it doesn't then its result is `undefined`.</li>
<li>Значение возвращается оператором `return ...`.</li>
<li>Вызов `return` тут же прекращает функцию.</li>
<li>Если `return;` вызван без значения, или функция завершилась без `return`, то её результат равен `undefined`.</li>
</ul> </ul>
При обращении к необъявленной переменной функция будет искать внешнюю переменную с таким именем, но лучше, если функция использует только локальные переменные: It is possible for a function to access variables defined outside of it.
But to make the code cleaner and easier to understand, it's recommended to use local variables and parameters instead as much as possible.
It is always easier to understand a function which gets parameters `a, b, c`, works with them and returns a result, than a function which gets no parameters, but modifies outer `a, b, c` somewhere in it's code.
Function naming:
<ul> <ul>
<li>Это делает очевидным общий поток выполнения -- что передаётся в функцию и какой получаем результат.</li> <li>A name should clearly describe what the function does. When we see a function call in the code, a good name instantly gives us an understanding what it does and returns.</li>
<li>Это предотвращает возможные конфликты доступа, когда две функции, возможно написанные в разное время или разными людьми, неожиданно друг для друга меняют одну и ту же внешнюю переменную.</li> <li>A function is an action, so function names are usually verbal.</li>
</ul> <li>There is a bunch of commonly adapted verbal prefixes like `create…`, `show…`, `get…`, `check…` etc which can help. The main point is to be consistent about their meaning.</li>
</li>
</ul> </ul>
Именование функций: Functions are the main building blocks of scripts. Now we covered the basics, so we actually can use them.
<ul> But we are going to return to them, going more deeply in their advanced features.
<li>Имя функции должно понятно и чётко отражать, что она делает. Увидев её вызов в коде, вы должны тут же понимать, что она делает.</li>
<li>Функция -- это действие, поэтому для имён функций, как правило, используются глаголы.</li>
</ul>
Функции являются основными строительными блоками скриптов. Мы будем неоднократно возвращаться к ним и изучать все более и более глубоко.