# Функции, области видимости

## Trivia
### Повторяющиеся действия.

Как можно было заметить, компьютеры сильны именно в однотипных задачах, и делают простые задачи очень быстро. Связанные друг с другом однотипные задачи
обычно
повторяются в циклах (однотипные операции над массивами данных, статистические задачи, отрисовка повторяющихся данных на экране и так далее). Так же есть
задачи *по требованию*, которые могут пригодится в любом месте кода. Например: `prompt`, `alert`, `Math.random` и прочие встроенные **функции**, которые
являются *подпрограммами*,  содержащими в себе программный код, вызываемый для решения определенной задачи. Я думаю понятно, что данные возможности
*не являются* возможностями аппаратуры, а воплощены на программном уровне.

### DRY

**Don't repeat yourself**. Один из основопологающих принципов разработки. Суть в том, что в процессе программирования вы должны минимизировать 
повторяющиеся *части кода*, которые делают почти одинаковые задачи; так как копипаста в коде приводит к дублированию отладки, да и вообще некрасиво это
:-)

### KISS
**Keep It Simple, Stupid**. Решайте задачи самым простым способом.
> Отладка кода вдвое сложнее, чем его написание. Так что если вы пишете код настолько умно, насколько можете, то вы по определению недостаточно сообразительны, чтобы его отлаживать.
> — *Brian W. Kernighan*.

### DRY > KISS

Зачастую эти принципы противоречат друг другу; уменьшение объема кода требует более мощных и сложнее отлаживаемых средств языка; однако в долгосрочной
перспективе принцип **DRY** полезней, чем простота кода (**KISS**).

### Пример

```javascript
var surname    = prompt("Введите фамилию","")
if (surname === null || surname === ""){
    surname    = "Иванов"
}

var name       = prompt("Введите имя","")      || "Иван"
var fathername = prompt("Введите отчество","") || "Иванович"
```

Это наш пример, который спрашивает у пользователя ФИО ИЛИ берет эти параметры по умолчанию. Как видите, алгоритм ввода ФИО  однотипен, и его
неплохо было бы выделить в **функцию**. Ко всему прочему, несмотря на эквивалентность алгоритма, `surname` вводится кодом, отличающимся от
ввода `name` и `fathername`, что усложняет модификацию и отладку кода.

### Задание

Порассуждаем о функциях, какие свойства должны быть у них, что бы они обеспечивали прозрачную работу в комбинации с другим кодом и не имели
непредсказуемых побочных эффектов для кода, который их использует.

# Ниже спойлер, имейте совесть :-D. Не омрачайте задание подглядыванием ответов.
![СПОЙЛЕР](http://www.websoldier.ru/wp-content/uploads/2015/02/kak-sdelat-spojler-dlya-sajta.jpg)
![СПОЙЛЕР](http://www.websoldier.ru/wp-content/uploads/2015/02/kak-sdelat-spojler-dlya-sajta.jpg)
![СПОЙЛЕР](http://www.websoldier.ru/wp-content/uploads/2015/02/kak-sdelat-spojler-dlya-sajta.jpg)
![СПОЙЛЕР](http://www.websoldier.ru/wp-content/uploads/2015/02/kak-sdelat-spojler-dlya-sajta.jpg)

## Функции

**Функция** - подпрограмма, которая принимает определенные параметры при вызове, выполняет определенный код, и возвращает выполнение кода в место вызова,
*опционально* (не обязательно) вернув результат работы в место вызова.

Свойства **функции**, которые сделали её такой полезной для написания программ:

- **Вызов**. Функция может быть вызвана, код функции выполнится в другом месте, после выполнения функции выполнение кода продолжается с места вызова.

```javascript
function d()
{
    debugger;
}
alert("before d");
d()
alert("after d");
d()
```
Для входа и выхода из функции используются `F11` и `Shift-F11` в **Developer Tools** при **пошаговой отладке**

- **Область видимости**. Так как функция не может "знать", из какого контекста она вызывается, то нет возможности знать заранее, совпадают ли имена
  переменных в функции и вне её. Таким образом вероятны *побочные эффекты* - непредсказуемые изменения переменных во внешнем коде, которые могут
  вызвать неправильную работу кода в целом;

```javascript
var surname = "Петров";
function readSurname()
{
    surname = prompt("Введите фамилию","")
    if (surname === null || surname === ""){
        surname = "Иванов"
    }
}


alert(surname);
readSurname();
alert(surname);
```

  Для решения этой проблемы используется концепция *области видимости* - **правильно** объявленная переменная
  в функции (через `var`) существует только в функции и создаются каждый раз при вызове функции; внешние же переменные с таким же именем остаются
  нетронутыми

```javascript
var surname = "Петров";
function readSurname()
{
    var surname = prompt("Введите фамилию","") // ТУТ
    if (surname === null || surname === ""){
        surname = "Иванов"
    }
}
alert(surname);
readSurname();
alert(surname);
```

- **Параметры** Функция должна уметь получить те или иные данные для своего выполнения. Например встроенные функции `confirm`, `prompt`, `alert`.
    **Задание**: Каковы параметры и какой у них смысл в вышеуказанных встроенных функциях?

```javascript
var surname = "Петров";
function readWithDefault(promptText, promptDefault, somethingDefault)
{
    var something = prompt(promptText,promptDefault)
    if (something === null || something === ""){
        something = somethingDefault;
    }
    alert("something: " + something);
}
alert(surname);
readWithDefault("Введите фамилию","","Иванов");
```

- **Возвращаемое значение**. Обратите внимание на то, что **функции** можно использовать как переменные в выражениях, однако не всегда это имеет смысл. 
  Более того, функции нельзя присвоить значение, однако можно *прочесть* её значение.

```javascript
var surname = "Петров";
function readWithDefault(promptText, promptDefault, somethingDefault)
{
    var something = prompt(promptText,promptDefault)
    if (something === null || something === ""){
        something = somethingDefault;
    }
    return something;
}
alert(surname);
var name = readWithDefault("Введите имя","","Иван");
alert(name);
```

## Определение и выполнение функций

Обратите внимание, что первый `alert` происходит ДО включения пошаговой отладки. Это говорит о том, что *определение функции* **НЕ** вызывает её. 
Код функции работает только после вызова, который происходит по d(). Для вызова надо указать в коде имя функции и скобки после имения (с параметрами
или без оных)
```javascript
function d()
{
    debugger;
}
alert("before d");
d()
alert("after d");
```

Определение начинается с ключевого слова `function`, после которого идет имя функции и параметры в скобках через запятую. Далее идет блок кода функции
в фигурных скобках. В отличие от `if`, `else` и циклов, фигурные скобки **обязательны**.

### Именование функций

Как и переменным, функциям нужно давать осмысленные названия. Только учтите, что переменные - *существительные* кода, а функции - *глаголы* кода.

### Выполнение функций.

Когда в коде упоминается имя функции со скобками и, возможно, параметрами происходят следующие действия:
- вычисляются выражения в скобках. В функцию попадают уже *значения* выражений.
- создается новая область видимости, в которую попадают параметры и их значения. Вам не нужно определять переменные для параметров.
- начинается выполнение кода в фигурных скобках определения функции. Все переменные, определенные через `var` попадают в локальную область видимости функции, не перекрывающую внешнюю область видимости.
- Код выполняется до выполнения `return` или окончания кода функции (закрывающей фигурной скобки). `return` прерывает выполнение функции, более того, 
  с помощью `return` происходит возврат значения функции, которое подставляется на место вызова функции. Таким образом функция ведет себя как 
  *переменная* для чтения. Если функция ничего не возвращает, то, на самом деле, она возвращает `undefined`

```javascript
function sqr(a){
    alert("Вы передали:" + a);
    return a*a;
    alert("Этот код не выполнится");
}  

var sqr1 = sqr(5)
var otherVar = 2;
alert("Сумма квадратов: " + (sqr1 + sqr(otherVar + otherVar)));
```

## Параметры функции и возвращаемое значение

### Параметры (аргументы)

Параметры функции перечисляются в скобках после имени через запятую. Параметры - это переменные области видимости функции, в которые попадают
вычисленные значения, передаваемые при **вызове**. Таким образом функции получают данные из внешнего кода.

В **Javascript** количество параметров при определении и при вызове может отличаться. В таком случае непереданные параметры равны `undefined`:
```javascript
debugger;
function add(a,b)
{
    a = a || 0;
    b = b || 0;
    return a + b;
}

alert(add())
alert(add(1));
alert(add(2,3));
```

Если же параметров больше, чем указано в определении функции, то ошибки тоже не происходит. Для доступа к остальным полям существует **псевдомассив**
`arguments`. 
```javascript
debugger;
function add(a,b)
{
    console.log(arguments)
    a = a || 0;
    b = b || 0;
    return a + b;
}

alert(add(4,5,6))
alert(add(4,5,6,7));

prompt("Введите число", "0");
prompt("Введите число");
```

**Задание**

Используя перебор массива `arguments` циклом `for`, сделайте функцию, которая складывает любое количество параметров

### Возвращаемое значение

Для возврата значения используется `return`. У него три основных свойства:
- Собственно возврат значения во внешний код. Выражение после `return` *вычисляется в контексте функции*:

```javascript
debugger;
function add(a,b)
{
    return a + b; 
}
alert(add(3,4))
```

после чего *значение* попадает в место, где функция была вызвана (в `alert`)
- Прекращение выполнения функции
- `return` без параметра возвращает *ничего*, т. е. `undefined`:

```javascript
debugger;
function bigAndWeirdFunction()
{
    var somethingBad = Math.random() > 0.5;
    if (somethingBad){
        alert("Something bad happens");
        return;
    }
    alert("All OK!");
}
bigAndWeirdFunction();
bigAndWeirdFunction();
bigAndWeirdFunction();
```

### `console.log` и `return`

При отладке вы видите в одной консоли  *вычисленное значение выражения* (например `2 + 2` или `prompt("Введите число")`) и вывод `console.log`. 
`console.log` *просто выводит текст* в консоль, как `document.write` - в окно браузера, далее вы с этим ничего не можете сделать (почти). 
**Выражение** же может быть вставлено в код и являться частью другого выражения:

```javascript
2 + 2
var a = 2 + 2
prompt("Введите число");
var num = prompt("Введите число");

var b;
    b = console.log(a); //неработает, метод log объекта console возвращает undefined, т. е. ничего
    b = a; //работает

function myLowerCase(str)
{
    console.log(str.toLowerCase()); //это просто пишет текст в консоли.
}

function rightUpperCase(str)
{
    return str.toUpperCase(); //это работает правильно
}

var lowerCase = myLowerCase("AbCdEf") //не работает.
var upperCase = rightUpperCase("AbCdEf") //работает
```

Что бы отличить результат выражения от вывода console.log, отметьте что возле значения выражения есть знак <.

## Область видимости

Как было указано выше, переменные, объявленные с `var` внутри функции, являются незаметными для окружающего кода и перекрывают совпадающие переменные
внутри функции, оставляя невредимыми внешние переменные:

```javascript
var a = 5;

alert("global a: " + a);
function someFunctionWithA(){
    var a = "string";
    alert("function scope a: " + a);
}

alert("global a after function declaration" + a);
someFunctionWithA()
alert("global a after function execution" + a);
```

Область видимости создается **каждый раз** при **вызове** функции:

```javascript

debugger;
function add(a,b)
{
    var result = a + b;
    return result;
}

add(1,2)
add(5,6)
```

Как видите, переменные `a`,`b` и `result` каждый раз имеют разные значения. При вызове область видимости создается, по выходу из функции - удаляется
(*не всегда*).

### Глобальная область видимости

Если переменная создается **без** `var` в *любом* месте кода, в том числе в функции, она является глобальной, т. е. видимой везде. 

```javascript
debugger;
function add(a,b)
{
    result = a + b;
    return result;
}

result = add(1,2)
alert(result);
add(5,6)
alert(result);
```

Как видно в примере выше, мы не можем расчитывать на целостность переменной `result`, пользуясь функцией `add`. Использование глобальных переменных
в большинстве случаев неоправдано; они нужны в-основном только для каких-то общих данных для чтения. Например `Math.PI` является глобальной переменной,
доступной только на чтение; то есть *константой*. Ваши же переменные будут доступны и на запись, будьте аккуратны используя их.

**Общее правило для новичка**: всегда ставьте `var`.


### Вложенные функции и их области видимости

```javascript 
var a = "0";
var b = "0";
var c = "0";

function level1(){
    var b = "1";
    var c = "1";

    function level2(){
        var c = "2";
        console.log("Level 2 scope: a: " + a + " b: " + b + " c: " + c);
    }
    level2();
    console.log("Level 1 scope: a: " + a + " b: " + b + " c: " + c);
}

level1();
console.log("Level 0 scope: a: " + a + " b: " + b + " c: " + c);
```
Проанализируйте вывод кода выше. Самая вложенная функция `level2` видит переменные своей области видимости (c), потом ищет значение на уровень
выше (для переменной `b`), и на уровень еще выше (для `a`). Промежуточная функция `level1` *ничего* не знает о переменных в `level2`, но видит свою
область видимости и глобальную. Глобальная же имеет свои переменные `a`, `b`, `c` в первозданном виде.

```javascript 
var a = "0";
var b = "0";
var c = "0";

function level1(){
    var b = "1";
    var c = "1";
    var d = "1";

    function level2(){
        var c = "2";
        var e = "2";
        console.log("Level 2 scope: a: " + a + " b: " + b + " c: " + c + " d: " + d + " e: " + e); 
    }
    level2();
    console.log("Level 1 scope: a: " + a + " b: " + b + " c: " + c + " d: " + d + " e: " + e);
}

level1();
console.log("Level 0 scope: a: " + a + " b: " + b + " c: " + c + " d: " + d + " e: " + e);
```

Данный пример иллюстрирует отсутствие переменных `e` в глобальной области видимости и `level1`, переменной `d` - в глобальной области видимости. Переменная
`d` попадает из `level1` в `level2`.

## Функции высшего порядка.

### Функция как тип данных.

Функции в **JS** являются *типом данных*, наряду с числами и строками. Определение функции является выражением и вычисляется как значение типа данных
`function`:

```javascript
function a(){
}
alert(typeof a);
```

Набор операций с функциями невелик, в отличие от строк их нельзя конкатенировать, нельзя складывать и умножать как числа; однако их можно присваивать
переменным и вызывать. **JS** позволяет создавать функции без названия:

```javascript
a();
function a(){
    console.log('declared func');
}

someFuncVariable()
var someFuncVariable = function (){
    console.log('anon func');
}

someFuncVariable()

var b = a;
a = null;
a()
a = b
a()
```

### Функции высшего порядка

**Функциями высшего порядка** называют функции, которые оперируют другими функциями - принимают их в качестве параметров или возвращают как результат 
выполнения. Такой подход позволяет произвести *инъекцию своего кода*. Например, все реализации алгоритма сортировки сравнивают разные сортируемые элементы,
при этом для работы алгоритма *вовсе не обязательно* знать структуру сортируемых данных; достаточно просто знать, какой элемент из двух *больше* или *меньше*.

Функция, передаваемая в качестве параметра другой функции для последующего вызова называется `callback`.

```javascript
var arrayOfNumbers = [4,18,10,2,-1,100, 0, 0.5];
arrayOfNumbers.sort(); //сортирует, используя обычное сравнение `<` и `>`

function absSort(a, b){
    return Math.abs(a) > Math.abs(b) ? 1 : -1;
}
arrayOfNumbers.sort(absSort); //сортировка по абсолютному значению
```

Первый `sort` выше сортирует, используя знаки `<` для элементов массива;

Второй `sort` принимает в качестве параметра функцию, которая вызывается внутри `sort` для некой пары сортируемых элементов. Пара выбирается согласно логике
алгоритма сортировки; выбор же, кто из этих двух элементов больше, а кто - меньше, возлагается на переданную функцию `absSort`, которая должна вернуть
1 если `а` считается больше `b` и -1 в обратном случае. В случае равенства `a` и `b` - возвращается 0.

Функция `absSort` сравнивает числа по *абсолютному значению*, т. е. по модулю. Таким образом самым маленьким числом в массиве является 0, потом 0.5, -1 и 
так далее.

Таким же образом мы можем отсортировать по тому или иному критерию массив объектов (ассоциативных массивов), например:

```javascript
var persons = [
    {name: "Иван", age: 17},
    {name: "Мария", age: 35},
    {name: "Алексей", age: 73},
    {name: "Яков", age: 12},
]
persons.sort(function(a,b){ //сортируем по возрасту
    if (a.age > b.age){
        return 1;
    }
    return -1;
});

persons.sort(function(a,b){ //сортируем по имени
    if (a.name > b.name){
        return 1;
    }
    return -1;
});
```

Рассмотрим пример:

```javascript
function intPrompt(message, defaultValue)
{
    do {
        var value = prompt(message, defaultValue);
    } while(isNaN(+value) || !Number.isInteger(+value) && value !== null);
    return value;
}

function validatedPrompt(message, defaultValue, validator)
{
    do {
        var value = prompt(message, defaultValue);
    } while(!validator(value) && value !== null);
    return value;
}
debugger;
alert(validatedPrompt("number", "", function(value) {
            return !isNaN(+value) && Number.isInteger(+value) 
}))

alert(validatedPrompt("камень-нжнцы-бмг", "", function(value) {
            return ["камень", "ножницы", "бумага"].indexOf(value.toLowerCase()) > -1;
}))
```

## Для чего используются функции.

- Для избавления повторяющихся кусков кода. **DRY**
- Для структуризации и задания имени какой-либо последовательности операций. Например зачастую в начале работы кода запускают однократно функцию
 `init` (имя приведено для примера), которая выполняется *один* раз, т. е. не уменьшает объем кода. Однако, таким образом, все действия, которые 
  относятся к подготовке программного окружения, заносятся в отдельный блок кода, что более наглядно
- Функции обратного вызова используются для внедрения кода, как в случае с `sort` и `validatedPrompt`
- Функции обратного вызова используются для того, что бы отказаться от опроса (poll) и перейти к событийной архитектуре (push), т. е. вместо
  постоянной проверки произошло то или иное событие или нет - происходит вызов callback когда это событие произошло.
- Функции используются для создания обособленной области видимости, что бы не нарушать окружающее пространство имен:

```javascript
(function(){
    var a = 5;
    var b = "100500";
})()
```
в данном примере создается функция и тут же вызывается. Функция не сохраняется не в какой из переменных, а значит вы не сможете её вызывать более
чем один раз. Единственная цель такой функции (**Self-Invoked Function**) - создать свою обособленную область видимости, в которой можно оперировать
любыми именами переменных не опасаясь побочных эффектов и влияния на переменные окружающего кода. Просто блок кода со своими именами.