Browse Source

10 Closures alpha

Ivan Asmer 7 years ago
parent
commit
f1fd79977a
1 changed files with 138 additions and 0 deletions
  1. 138 0
      10Closures.md

+ 138 - 0
10Closures.md

@@ -0,0 +1,138 @@
+# Замыкания, приватные методы и данные.
+
+Функция в **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**.
+
+## makeCounter 
+
+```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());
+```