# ООП функциональное. Замыкания ## `call` и `apply` Эти два метода объекта `Function` позволяют вызвать функцию, указав `this` и параметры: ```javascript function Person(name, surname){ this.name = name this.surname = surname this.getFullName = function(){ return this.name + (this.fatherName ? " " + this.fatherName + " " : " ") + this.surname } } function say(greet, aftergreet){ alert(greet + " " + this.getFullName() + aftergreet) } var person = new Person("Ivan", "Petroff") say.call(person,"Hello", "!!111!!!") say.apply(person,["Hi", "by apply"]) //feel the difference ``` То, что `apply` принимает массив в качестве набора параметров функции помогает **творить чудеса**: ```javascript var someArray = [1,5,7,-17,100500]; Math.min(someArray) //Math.min так не умеет Math.min(1,5,7,-17,100500,-123) // зато умеет так Math.min.apply(Math,someArray) // ...и мы этим воспользуемся... // или же сделаем свой min в someArray: someArray.min = function(){ return Math.min.apply(Math,someArray); } someArray.min() // или же сделаем свой min для всех массивов Array.prototype.min = function(){ return Math.min.apply(Math, this) }; [100, 500, -100500].min() ``` ## Замыкания, приватные методы и данные. Функция в **JS** находится сразу в двух контекстах: *динамическом* (`this`, значения параметров) и *лексическом* (переменные из более высоких областей видимости в **месте определения функции**). *Динамический контекст* - это контекст **вызова** функции - значение параметров и окружение на момент вызова; *лексический контекст* - контекст **определения** функции, её вложенности в другие области видимости, доступ к которым функция имеет и после окончания выполнения функций-владельцев этих областей видимости. ### makeAdder ```javascript function makeAdder(x){ function adder(y){ return x + y; } return adder } var add5 = makeAdder(5); var greeter = makeAdder("Hi, "); alert(add5(2)); alert(greeter("John")); ``` В примере выше `5`,`"Hi, "`, `2` и `"John"` находятся в динамическом контексте вызова функции. А вот значение `x` в `adder` находится в *лексическом* контексте. На момент запуска функций `add5` и `greeter` функции `makeAdder` уже давно отработали, однако значение `x` и область видимости продолжают присутствовать в функциях `add5` и `greeter`. Это называется **замыканием** - данные в локальной области видимости отработавшей функции и код, который использует эти данные впоследствии. В широком смысле **замыкание** является *объектом*, так как хранит в единой сущности код и данные. В контексте **JS** *замыкания* очень удобны для огораживания кода, сохранения состояния переменных "на будущее", для функций обратного вызова. Так же *замыкания* удобны для организации приватных полей у объектов **JS**. ```javascript function makeCounter(){ var counter = 0; return function(){ return counter++; } } ``` Функция выше создает счетчик, значение которого можно узнать из возвращаемой анонимной функции. При этом счетчик увеличится на 1. Если расширить функционал данного примера для чтения и декремента счетчика, получим, например: ```javascript function makeCounter(){ var counter = 0; function inc(){ return counter++; } function dec(){ return counter--; } function read(){ return counter; } return [inc,dec,read]; } ``` Результатом выполнения `makeCounter` будет *массив функций*. Однако намного более наглядным будет создание именованного массива, т. е. объекта: ```javascript function makeCounter(){ var counter = 0; function inc(){ return counter++; } function dec(){ return counter--; } function read(){ return counter; } return {inc: inc, dec: dec, read: read}; } ``` ... что во многом схоже с обычным объектом сделанным через **функцию-конструктор**: ```javascript function Person(name, surname){ this.name = name this.surname = surname var age = 0 function checkNewAge(){ return (!isNaN(this.newAge) && Number.isInteger(this.newAge) && (this.newAge > 0) && (this.newAge < 100)) } this.setAge = function(newAge){ this.newAge = +newAge if (checkNewAge()){ age = this.newAge; } return age } this.getAge = function(){ return age } this.getOriginalName = function(){ return name; } this.getOriginalSurname = function(){ return surname; } } var person = new Person("Ivan", "Petroff") alert(person.setAge(50)) alert(person.setAge(125)) alert(person.setAge(25)) person.name = "John"; person.surname = "Doe"; alert(person.getOriginalName()); alert(person.getOriginalSurname()); ``` ## Рекурсия и её отладка Рекурсия - это прямой или косвенный самовызов функции. Обычно применяется для обработки деревьев в структурах данных или иных вложенностей. Например **факториал** (`!`) - произведение всех чисел от `1` до `N` можно определить как: N ! = 1 x 2 x 3 x 4 x .... x N, или рекурсивно: N ! = N x (N - 1) ! ```javascript function factorial(N){ if (N <= 1){ return 1; } return N * factorial(N -1); } factorial ( 5 ); // это 5 * factorial(4), что, в свою очередь, будет 4 * factorial(3) и так далее ``` При отладке рекурсий обращайте внимания на вложенность вызовов (Call Stack) и областей видимостей (Scopes).