Замыкание (программирование)

Из Википедии, бесплатной энциклопедии

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

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

Замыкание — это особый вид функции. Она определена в теле другой функции и создаётся каждый раз во время её выполнения. Синтаксически это выглядит как функция, находящаяся целиком в теле другой функции. При этом вложенная внутренняя функция содержит ссылки на локальные переменные внешней функции. Каждый раз при выполнении внешней функции происходит создание нового экземпляра внутренней функции, с новыми ссылками на переменные внешней функции.

В случае замыкания ссылки на переменные внешней функции действительны внутри вложенной функции до тех пор, пока работает вложенная функция, даже если внешняя функция закончила работу, и переменные вышли из области видимости.[1]

Замыкание связывает код функции с её лексическим окружением (местом, в котором она определена в коде). Лексические переменные замыкания отличаются от глобальных переменных тем, что они не занимают глобальное пространство имён. От переменных в объектах они отличаются тем, что привязаны к функциям, а не объектам.

Примеры[править | править код]

Больше примеров смотрите в викиучебнике.

В языке Scheme[править | править код]

(define (make-adder n)       ; возвращает замкнутое лямбда-выражение   (lambda (x)                ; в котором x - связанная переменная,     (+ x n)                  ; а n - свободная (захваченная из внешнего контекста)   ) )                  (define add1 (make-adder 1)) ; делаем процедуру для прибавления 1 (add1 10)                    ; вызываем её, возвращает 11  (define sub1 (make-adder -1)); делаем процедуру для вычитания 1 (sub1 10)                    ; вызываем её, возвращает 9 

В языке JavaScript[2][править | править код]

'use strict';  const add = function(x) {   return function(y) {     const z = x + y;     console.log(x + '+' + y + '=' + z);     return z;   }; };  const res = add(3)(6); // вернёт 9 и выведет в консоль 3+6=9  console.log(res); 

Этот же код в версии ECMAScript2015 с использованием "стрелочных функций":

'use strict';  const add = x => y => {   const z = x + y;   console.log(x + '+' + y + '=' + z);   return z; };  const res = add(3)(6); // вернёт 9 и выведет в консоль 3+6=9  console.log(res); 

Пояснение: в JavaScript сочетание => является оператором объявления стрелочной функции, см например https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Functions/Arrow_functions. Здесь в константу add помещается функция от аргумента x, результатом которой будет являться другая функция, а именно функция от аргумента y, результат которой вычисляется приведённым в фигурных скобках блоком кода. Этот блок кода опирается на аргумент y своей функции и на замыкание, создаваемое для аргумента x внешней функции.

При вызове add(3)(6) функция, хранящаяся в переменной add, вызывается с аргументом 3 и возвращает функцию, завязанную на значение 3 в замыкании x.

Далее в рамках такого обращения эта функция выполняется с аргументом y = 6 и возвращает 9.

Можно сделать рекурсивное замыкание:

'use strict';  const add = x => y => {   const z = x + y;   console.log(x + '+' + y + '=' + z);   return add(z); };  const res = add(1)(4)(6)(9);  console.log(res); /* 1+4=5    5+6=11    11+9=20    [Function]*/ 

Когда JS-код работает — локальные переменные хранятся в scope. В JavaScript локальные переменные могут оставаться в памяти даже после того, как функция вернула значение.

Все функции в JavaScript это замыкания, то есть всегда, когда создается функция — всегда создается замыкание, хоть и зачастую оно пустое, так как функции обычно из объявления контекста как правило ничего не используют. Но нужно понимать разницу между созданием замыкания и созданием нового scope-объекта: замыкание (функция + ссылка на текущую цепочку scope-объектов) создается при определении функции, но новый scope-объект создается (и используется для модификации цепочки scope-объектов замыкания) при каждом вызове функции.

В языке PHP[править | править код]

В PHP замыкания - это анонимные функции, особые конструкции, которые позволяют описывать функции, не имеющие определённых имён.

<?php  function add($x) {     return function ($y) use ($x) { // <-- анонимная функция (замыкание)         return $x + $y;     }; // <-- эта точка с запятой здесь нужна! }  echo add(3)(5) . PHP_EOL;   // Выведет: 8  $f = add(3); var_dump($f);               // Выведет: object(Closure) echo $f(6) . PHP_EOL;       // Выведет: 9 

В PHP наследование переменных из родительской области видимости осуществляется с помощью конструкции use путем явного указания имен наследуемых переменных.

Другой пример с передачей замыкания в метод, где ожидается callable-параметр:

<?php  function power($arr, $exp) {     // переменная $func будет хранить ссылку на объект класса Closure, который описывает наше замыкание     $func = function ($el) use ($exp) {         return $el ** $exp;     };          return array_map($func, $arr); }  $list = [1, 3, 4];  var_dump(power($list, 2)); // Выведет: array(3) {[0]=>int(1) [1]=>int(9) [2]=>int(16)} var_dump(power($list, 3)); // Выведет: array(3) {[0]=>int(1) [1]=>int(27) [2]=>int(64)} 

См. также[править | править код]

Примечания[править | править код]

  1. Blocks Can Be Closures — Containers, Blocks, and Iterators — Programming Ruby. The Pragmatic Programmer’s Guide. Дата обращения: 29 сентября 2011. Архивировано 23 сентября 2011 года.
  2. Closure: Function closures and storing data in function scope. — 2018-01-08. Архивировано 29 ноября 2019 года.