Функции 2 / Function 2

Замыкание / Сlosure

Замыкания — это функции, ссылающиеся на независимые (свободные) переменные. Другими словами, функция, определённая в замыкании, «запоминает» окружение, в котором она была создана.

Независимые переменные — это все переменные, которые не были переданы как параметры и не были объявлены как локальные.

Для понимания замыкания рассмотрим фазы жизненного цикла функции

Создание функции

Создание функции состоит из описания - сигнатура + блок, а также записи свойств this + [[Scope]]

Свойство this указывает на объект, в котром находится функция изучим позднее

Свойство [[Scope]] недоступно для обращения в JS записывается окружение функции вне самой функции (т.е. ИМЕНА независимых переменных)

Присвоение функции переменной


function sum (a) {
     return a + x; 
}

let add = sum;
add(2)   
Exception: 
  ReferenceError: 
     x is not defined       
                    

let x = 0;                
function sum (a) {
     return a + x; 
}

let add = sum;
add(2)   0

x = 2;
add(2)   4          
                    

При присвоении функции переменной (т.е. присвоение имени без вызова функции) происходит передача ссылки на объект функции.

Другимим словами - при присвоении функции переменной этой самой переменной передается и свойстов [[Scope]] со ссылкой на окружение функции на момент создания. Т.е. присвоение по ссылке - это не копирование.

Вызов функции

При вызове функции происходит создание и заполнение arguments и LexicalEnvironment или лексического окружения это свойство недоступно для обращения в JS..

Лексическое окружение - это все доступные ЗНАЧЕНИЯ имен независимых переменных.

В LexicalEnvironment при вызове также записвается и [[Scope]] с окружением на момент создания.

Другими словами - функция помнит ИМЕНА окружения на момент своего создания и заполняет их ЗНАЧЕНИЯМИ на момент вызова.

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

Область видимости
 
const common 
  = "Видима всем в window";

function Foo () {
  const fooScope 
    = "Видима " 
      + "только тем, " 
      + "кто внутри Foo";

  function Bar () {
      const barScope 
       = "Видима " 
         +"только тем, "
         + "кто внутри Bar";
  }
}        
                        
Подъем видимости
 
const common = 100;

function Foo () {
  const common = 200;

   function Bar () {
      const common = 300;
      return common;
  }

  return Bar;
}        
                        

После выполнения функции все ее LexicalEnvironment (значения независимых переменных и аргументы) удаляются

Создаем замыкание

Каррироваие - это просто

Каррирование - это способ сделать из вызова foo(a,b,c) вызов foo(a)(b)(c)

Сделать это очень просто - нужно сделать внутри функции return другой функции с одним из параметров. А затем можно повторить return с новым параметром вунтри вложенной функции. Т.е. сколько параметров - столько и возвратов вложенных функций

Каррироваие
 
pow(base, exponent)                
function pow(base) {
  function mult (exponent) {
    return base ** exponent;
  }
  return mult;
}

const toPow = pow;
toPow(2)

base = arguments[0];

function mult (exponent) {
  return base ** exponent;
}   


toPow(2)(4); 16               
            

Частичное применение - это спостоб присвоить одному из параметров постоянное значение для новой функции

Частичное применение
 
pow(base, exponent)                
function pow(base) {
  function mult (exponent) {
    return base ** exponent;
  }
  return mult;
}

const twoPow = pow(2);
twoPow(3); 8
            
Проверим видимость переменных
 
const common = 100;

function Foo () {
  const common = 200;

   function Bar () {
      const common = 300;
      return common;
  }

  return Bar;
}  

const Ups = Foo;
Ups()
function Bar () {
  const common = 300;
  return common;
}

Ups()() 300

                

Замыкание

Счетчик на основе замыкания
 
function addOne () {
внутренняя переменная
  let inCounter = 0;
  
внутренняя функция  
видит переменную  
  function increment() {
изменяет и возвращает        
   return inCounter += 1;
  }
  
  function set(val) {
    inCounter = val;
  }
  
   function get() {
    return inCounter;
  }
  
   return increment;
}

const uno = addOne();
uno(); 1
uno(); 2
uno(); 3

const duo = addOne();
duo(); 1                
duo(); 2                
duo(); 3                
                

Стрелочная функция / Arrow Function

Синтаксис

            
const sum = function (a, b) {
            return a + b;            
        }
            

Все, отмеченное фоном у обычной функции заменяется на => у стрелочной функции

Без фигурных скобок и return


const sum = (a, b) => a + b;           
            

Со скобками и return

При наличии скобок обязательно нужно ставить return, иначе вернется undefined


const sum = (a, b) => {
    return a + b;
}               
            

Без параметров или fat синтаксис


const sum = () => a + b;
            

Один аргумент или необязательніе () скобки


const log = msg => 
         console.log(msg);
            

Три "нет" Arrow function

  1. Отсутствует arguments
  2. Не имеет собственного this this по месту создания
  3. Не рабтает с new и, как следствие, не рабоатет с super

Немного практики со стрелочными функциями

Функция обратного вызова / Callback

Параметр и аргумент

Функция принимает данные, преобразовывает данные и возвращает данные.

В JavaScript вместо данных функции можно передать другую функцию как аргумент, ведь та в свою очередь все равно вместо себя подставит данные.

Функция как аргумент
 
const double = (param) 
    => param + param;

const log = (str) =>
    console.log(str);  
log(double("a")); aa               
            

Функция как параметр

Также JavaScript вместо данных можно указать на применение какой-то функции как параметр. А при вызове подставить конкретную функцию как аргумент. В итоге все равно будут передаваться вычисленные данные.

Функция как параметр
 

const  add = param 
  => param + param;

const  mult = param 
  => param * param;

const multFoo = (num, foo)
  => num * foo(num);

multFoo(3, add) 18
multFoo(3, mult) 27              
            

Функция, как значение массива

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

Пример
 
const array = [
    a => a + a,
    a => a * a,
    a => a / a,
    a => a**a
]      
const ariphmetic = (arr, val) 
    => arr.map(
        elem => elem(val)
);   
ariphmetic(array, 3)              
 [6, 9, 1, 27]
            

Пример работы callback в браузере

Этот пример служит для демонстрации эксклюзивных возможностей callbak. Не все в этом примере изучено. Полное объяснение работы с DOM и событиями будет позже. Этот пример максимально прост и не рекомендутся к повторению.