Что такое «закрытие»?


432

Я задал вопрос о карринге и были упомянуты закрытия. Что такое закрытие? Как это связано с карри?


22
Теперь, что именно закрытие ??? В некоторых ответах говорится, что закрытие - это функция. Некоторые говорят, что это стек. В некоторых ответах говорится, что это «скрытая» ценность. Насколько я понимаю, это функция + вложенные переменные.
Роланд

3
Объясняется, что такое закрытие: stackoverflow.com/questions/4103750/…
dietbuddha

Также посмотрите на Что такое закрытие? на softwareengineering.stackexchange
B12Toaster

Объясняется, что такое закрытие и общий случай использования: trungk18.com/experience/javascript-closure
Sasuke91

Ответы:


744

Переменная область

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

function() {
  var a = 1;
  console.log(a); // works
}    
console.log(a); // fails

Если я пытаюсь получить доступ к локальной переменной, большинство языков будет искать ее в текущей области, а затем через родительские области, пока они не достигнут корневой области.

var a = 1;
function() {
  console.log(a); // works
}    
console.log(a); // works

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

Так мы обычно ожидаем, что все будет работать.

Закрытие - это постоянная область видимости локальной переменной

Закрытие - это постоянная область действия, которая сохраняет локальные переменные даже после того, как выполнение кода вышло из этого блока. Языки, которые поддерживают закрытие (такие как JavaScript, Swift и Ruby), позволят вам сохранить ссылку на область (включая ее родительские области) даже после того, как завершится выполнение блока, в котором были объявлены эти переменные, при условии сохранения ссылки в этот блок или функцию где-то.

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

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

Например

Вот действительно простой пример в JavaScript, который иллюстрирует это:

outer = function() {
  var a = 1;
  var inner = function() {
    console.log(a);
  }
  return inner; // this returns a function
}

var fnc = outer(); // execute outer to get inner 
fnc();

Здесь я определил функцию внутри функции. Внутренняя функция получает доступ ко всем локальным переменным внешней функции, включая a. Переменная aнаходится в области видимости для внутренней функции.

Обычно при выходе из функции все ее локальные переменные стираются. Однако если мы возвращаем внутреннюю функцию и присваиваем ее переменной, fncчтобы она сохранялась после outerвыхода, все переменные, которые находились в области видимости, когда она innerбыла определена, также сохраняются . Переменная aбыла закрыта - она ​​находится в закрытии.

Обратите внимание, что переменная aполностью приватна для fnc. Это способ создания частных переменных в функциональном языке программирования, таком как JavaScript.

Как вы уже могли догадаться, при вызове fnc()он печатает значение a, равное «1».

В языке без замыканий переменная aбыла бы собрана и отброшена при outerвыходе из функции . Вызов fnc вызвал бы ошибку, потому что aбольше не существует.

В JavaScript переменная aсохраняется, поскольку область видимости переменной создается при первом объявлении функции и сохраняется до тех пор, пока функция продолжает существовать.

aпринадлежит к сфере outer. Область действия innerимеет родительский указатель на область действия outer. fncпеременная, которая указывает на inner. aсохраняется до тех пор, пока fncсохраняется. aнаходится в закрытии.


116
Я подумал, что это довольно хороший и простой для понимания пример.
user12345613

16
Спасибо за потрясающее объяснение, я видел много, но это время, когда я действительно получил это.
Димитар Димитров

2
Могу ли я привести пример того, как это работает в библиотеке, такой как JQuery, как указано во втором абзаце? Я не совсем понял это.
DPM

6
Привет, Джуббат, да, открой jquery.js и взгляни на первую строку. Вы увидите, что функция открыта. Теперь перейдите к концу, вы увидите window.jQuery = window. $ = JQuery. Затем функция закрывается и выполняется самостоятельно. Теперь у вас есть доступ к функции $, которая, в свою очередь, имеет доступ к другим функциям, определенным в замыкании. Это отвечает на ваш вопрос?
Сверхсветовой

4
Лучшее объяснение в сети. Намного проще, чем я думал
Mantis

95

Я приведу пример (в JavaScript):

function makeCounter () {
  var count = 0;
  return function () {
    count += 1;
    return count;
  }
}

var x = makeCounter();

x(); returns 1

x(); returns 2

...etc...

Что делает эта функция, makeCounter, так это то, что она возвращает функцию, которую мы назвали x, которая будет подсчитывать по одной каждый раз, когда она вызывается. Поскольку мы не предоставляем никаких параметров для x, он должен каким-то образом запоминать счет. Он знает, где его найти, основываясь на том, что называется лексической областью видимости - он должен искать место, где он определен, чтобы найти значение. Это «скрытое» значение - это то, что называется замыканием.

Вот мой пример карри:

function add (a) {
  return function (b) {
    return a + b;
  }
}

var add3 = add(3);

add3(4); returns 7

Вы можете видеть, что когда вы вызываете add с параметром a (который равен 3), это значение содержится в закрытии возвращаемой функции, которую мы определяем как add3. Таким образом, когда мы вызываем add3, он знает, где найти значение для выполнения сложения.


4
IDK, какой язык (вероятно, F #) вы использовали на вышеуказанном языке. Не могли бы вы привести приведенный выше пример в псевдокоде? Мне трудно понять это.
пользователь


3
@KyleCronin Отличный пример, спасибо. В: Правильнее ли сказать, что «скрытое значение называется замыканием» или «функция, которая скрывает значение, является замыканием»? Или «процесс сокрытия стоимости есть закрытие»? Спасибо!

2
@RobertHume Хороший вопрос. Семантически термин «закрытие» несколько двусмыслен. Мое личное определение заключается в том, что сочетание скрытого значения и использования его функцией-оболочкой составляет замыкание.
Кайл Кронин

1
@KyleCronin Спасибо - у меня есть схема в середине срока в понедельник. :) Хотел, чтобы концепция "замыкания" была в моей голове. Спасибо за размещение этого отличного ответа на вопрос ОП!

58

Ответ Кайла довольно хорош. Я думаю, что единственным дополнительным разъяснением является то, что замыкание - это в основном снимок стека в момент создания лямбда-функции. Затем при повторном выполнении функции стек восстанавливается до этого состояния перед выполнением функции. Таким образом, как упоминает Кайл, это скрытое значение ( count) доступно при выполнении лямбда-функции.


14
Это не просто стек - это окружающие лексические области видимости, которые сохраняются независимо от того, хранятся они в стеке или в куче (или в обеих).
Мэтт Фенвик

38

Прежде всего, вопреки тому, что большинство людей здесь говорят вам, закрытие не является функцией ! И что же это?
Это набор символов, определенных в «окружающем контексте» функции (известный как ее окружение ), которые делают его выражением CLOSED (то есть выражением, в котором каждый символ определен и имеет значение, поэтому его можно оценить).

Например, когда у вас есть функция JavaScript:

function closed(x) {
  return x + 3;
}

это закрытое выражение, потому что все символы, встречающиеся в нем, определены в нем (их значение ясно), так что вы можете оценить его. Другими словами, это автономно .

Но если у вас есть такая функция:

function open(x) {
  return x*y + 3;
}

это открытое выражение, потому что в нем есть символы, которые не были определены в нем. А именно, y. Рассматривая эту функцию, мы не можем сказать, что yесть и что это значит, мы не знаем ее значения, поэтому мы не можем оценить это выражение. Т.е. мы не можем вызвать эту функцию, пока не скажем, чтоy в ней должно означать. Это yназывается свободной переменной .

Эта y требует определения, но это определение не является частью функции - оно определяется где-то еще, в своем «окружающем контексте» (также известном как среда ). По крайней мере, это то, на что мы надеемся: P

Например, это может быть определено глобально:

var y = 7;

function open(x) {
  return x*y + 3;
}

Или это может быть определено в функции, которая оборачивает это:

var global = 2;

function wrapper(y) {
  var w = "unused";

  return function(x) {
    return x*y + 3;
  }
}

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

В приведенном выше примере внутренняя функция (которую мы не дали имени, потому что она нам не нужна) является открытым выражением, потому что переменная yв ней свободна - ее определение находится вне функции, в функции, которая ее оборачивает , среда для этой анонимной функции является множество переменных:

{
  global: 2,
  w: "unused",
  y: [whatever has been passed to that wrapper function as its parameter `y`]
}

Теперь замыкание - это та часть этой среды, которая закрывает внутреннюю функцию, предоставляя определения для всех ее свободных переменных . В нашем случае единственной свободной переменной во внутренней функции былаy , поэтому закрытие этой функции - это подмножество ее окружения:

{
  y: [whatever has been passed to that wrapper function as its parameter `y`]
}

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

Больше на теории позади этого здесь: https://stackoverflow.com/a/36878651/434562

Стоит отметить, что в приведенном выше примере функция-обертка возвращает свою внутреннюю функцию в качестве значения. Момент, когда мы вызываем эту функцию, может быть удален во времени с момента, когда функция была определена (или создана). В частности, его функция обертки больше не работает, а ее параметры, которые были в стеке вызовов, больше не присутствуют: P Это создает проблему, потому что внутренняя функция должна yбыть там, когда она вызывается! Другими словами, требуется, чтобы переменные из своего замыкания как-то пережили функцию-обертку и были там, когда это необходимо. Следовательно, внутренняя функция должна сделать снимок этих переменных, которые закрывают их, и хранить их где-нибудь в безопасности для дальнейшего использования. (Где-то за пределами стека вызовов.)

И именно поэтому люди часто путают термин « закрытие» с тем, что это особый тип функции, который может делать такие снимки внешних переменных, которые они используют, или структуры данных, используемой для хранения этих переменных для дальнейшего использования. Но я надеюсь, что теперь вы понимаете, что это не само замыкание - это просто способы реализации замыканий в языке программирования или языковые механизмы, которые позволяют переменным из замыкания функции быть там, когда это необходимо. Есть много заблуждений относительно замыканий, которые (излишне) делают этот предмет намного более запутанным и сложным, чем он есть на самом деле.


1
Аналогия, которая может помочь новичкам в этом, заключается в том, что замыкание связывает все свободные концы , что и делает человек, когда он ищет закрытие (или он разрешает все необходимые ссылки, или ...). Ну, это помогло мне думать об этом так: о)
Уилл Кроуфорд

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

29

Закрытие - это функция, которая может ссылаться на состояние в другой функции. Например, в Python это использует замыкание «внутреннее»:

def outer (a):
    b = "variable in outer()"
    def inner (c):
        print a, b, c
    return inner

# Now the return value from outer() can be saved for later
func = outer ("test")
func (1) # prints "test variable in outer() 1

23

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

Для начала я должен представить концепцию пространства имен. Когда вы вводите команду в интерпретатор Scheme, она должна оценить различные символы в выражении и получить их значение. Пример:

(define x 3)

(define y 4)

(+ x y) returns 7

Определения выражений хранят значение 3 в месте для x и значение 4 в месте для y. Затем, когда мы вызываем (+ xy), интерпретатор ищет значения в пространстве имен и может выполнить операцию и вернуть 7.

Однако в Scheme есть выражения, которые позволяют временно переопределить значение символа. Вот пример:

(define x 3)

(define y 4)

(let ((x 5))
   (+ x y)) returns 9

x returns 3

Что делает ключевое слово let, так это вводит новое пространство имен с x в качестве значения 5. Вы заметите, что он по-прежнему может видеть, что y равен 4, что делает возвращаемую сумму равной 9. Вы также можете видеть, что когда выражение закончилось x вернулся к 3. В этом смысле х был временно замаскирован локальным значением.

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

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

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

(define x 3)

(define (plus-x y)
  (+ x y))

(let ((x 5))
  (plus-x 4)) returns ?

Мы определяем x как 3 и плюс-x как его параметр, y, плюс значение x. Наконец, мы вызываем plus-x в среде, где x был замаскирован новым x, этот оценивается как 5. Если мы просто сохраняем операцию (+ xy) для функции plus-x, так как мы находимся в контексте если x равен 5, возвращаемый результат будет равен 9. Это то, что называется динамической областью видимости.

Однако Scheme, Common Lisp и многие другие языки имеют так называемую лексическую область видимости - в дополнение к сохранению операции (+ xy) мы также сохраняем пространство имен в этой конкретной точке. Таким образом, когда мы ищем значения, мы видим, что x, в этом контексте, действительно 3. Это закрытие.

(define x 3)

(define (plus-x y)
  (+ x y))

(let ((x 5))
  (plus-x 4)) returns 7

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


Хорошо, благодаря вашему ответу, я думаю, что наконец-то поняла, что такое закрытие. Но есть один большой вопрос: «мы можем использовать связанный список для хранения состояния пространства имен во время определения функции, что позволяет нам получать доступ к переменным, которые иначе больше не были бы в области видимости». Why do we want to access variables that are out of scope? when we say let x = 5, we want x to be 5 and not 3. What is happening?
Лазер

@Laser: Извините, это предложение не имело особого смысла, поэтому я обновил его. Надеюсь, теперь это имеет больше смысла. Кроме того, не думайте о связанном списке как о деталях реализации (так как он очень неэффективен), а как о простом способе осмысления того, как это можно сделать.
Кайл Кронин

10

Вот реальный пример того, почему Closures надрывают задницу ... Это прямо из моего кода Javascript. Позвольте мне проиллюстрировать.

Function.prototype.delay = function(ms /*[, arg...]*/) {
  var fn = this,
      args = Array.prototype.slice.call(arguments, 1);

  return window.setTimeout(function() {
      return fn.apply(fn, args);
  }, ms);
};

А вот как вы бы это использовали:

var startPlayback = function(track) {
  Player.play(track);  
};
startPlayback(someTrack);

Теперь представьте, что вы хотите, чтобы воспроизведение началось с задержкой, например, через 5 секунд после запуска этого фрагмента кода. Ну, это легко, delayи это закрытие:

startPlayback.delay(5000, someTrack);
// Keep going, do other things

Когда вы вызываете delayс помощью 5000ms, запускается первый фрагмент и сохраняет переданные аргументы в его замыкании. Затем, через 5 секунд, когда setTimeoutпроисходит обратный вызов, замыкание все еще поддерживает эти переменные, поэтому он может вызывать исходную функцию с исходными параметрами.
Это тип карри или функционального оформления.

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


1
Следует отметить, что расширение языка или хост-объектов, как правило, считается плохой вещью, поскольку они являются частью глобального пространства имен
Джон Кук

9

Функции, не содержащие свободных переменных, называются чистыми функциями.

Функции, содержащие одну или несколько свободных переменных, называются замыканиями.

var pure = function pure(x){
  return x 
  // only own environment is used
}

var foo = "bar"

var closure = function closure(){
  return foo 
  // foo is a free variable from the outer environment
}

источник: https://leanpub.com/javascriptallongesix/read#leanpub-auto-if-functions-without-free-variables-are-pure-are-closures-impure


Почему это используется? На самом деле, он гораздо более «на правильном пути» с этим различием в свободные переменные и связанные переменные, а также в чистые / закрытые функции и нечистые / открытые функции, чем большинство других неразумных ответов здесь: P (исключая путаницу замыканий с функциями быть закрытым).
SasQ

Понятия не имею , правда. Вот почему StackOverflow отстой. Просто посмотрите на источник моего ответа. Кто бы мог поспорить с этим?
soundyogi

ТАК не сосет и я никогда не слышал о термине "свободная переменная"
Кай

Трудно говорить о замыканиях без упоминания свободных переменных. Просто посмотрите их. Стандартная терминология CS.
ComDubh

«Функции, содержащие одну или несколько свободных переменных, называются замыканиями», хотя это неправильное определение - замыкания всегда являются объектами первого класса.
ComDubh

7

ТЛ; др

Замыкание - это функция, и ее область действия назначается (или используется как) переменной. Таким образом, закрытие имени: область действия и функция заключены и используются так же, как и любая другая сущность.

Подробное объяснение стиля Википедии

Согласно Википедии, закрытие :

Методы реализации привязки лексически ограниченных имен в языках с первоклассными функциями.

Что это обозначает? Давайте посмотрим на некоторые определения.

Я объясню замыкания и другие связанные определения, используя этот пример:

function startAt(x) {
    return function (y) {
        return x + y;
    }
}

var closure1 = startAt(1);
var closure2 = startAt(5);

console.log(closure1(3)); // 4 (x == 1, y == 3)
console.log(closure2(3)); // 8 (x == 5, y == 3)

Первоклассные функции

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

В приведенном выше примере startAtвозвращает ( анонимную ) функцию, которой назначены функции closure1и closure2. Итак, как вы видите, JavaScript обрабатывает функции так же, как и любые другие объекты (первоклассные граждане).

Привязка к имени

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

В приведенном выше примере:

  • В области внутренней анонимной функции, y в обязан3 .
  • В startAtобласти видимости, xсвязан 1или 5(в зависимости от закрытия).

Внутри области действия анонимной функции xнет привязки к какому-либо значению, поэтому ее необходимо разрешить в верхней startAtобласти.

Лексический обзор

Как говорит Википедия , сфера:

Это область компьютерной программы, где привязка действительна: где имя может использоваться для ссылки на объект .

Есть две техники:

  • Лексическая (статическая) область видимости: определение переменной определяется путем поиска в ее содержащем блоке или функции, затем, если это не удается, во внешнем содержащем блоке и т. Д.
  • Динамическая область видимости: выполняется поиск вызывающей функции, затем функция, вызвавшая эту вызывающую функцию, и так далее, прогрессирующая вверх по стеку вызовов.

Для более подробного объяснения проверьте этот вопрос и посмотрите на Википедию .

В приведенном выше примере мы видим, что JavaScript имеет лексическую область видимости, потому что при xразрешении привязка ищется в верхней области ( startAt-ях) на основе исходного кода (анонимная функция, которая ищет x, определяется внутри startAt) и не зависит от стека вызовов, способа (области, где) функция была вызвана.

Заворачивание

В нашем примере, когда мы вызываем startAt, он возвращает (первоклассную) функцию, которая будет назначена, closure1и, closure2таким образом, создается закрытие, потому что переданные переменные 1и 5будут сохранены в startAtобласти видимости, которая будет заключена в возвращаемую анонимная функция. Когда мы вызываем эту анонимную функцию через closure1и closure2с одним и тем же аргументом ( 3), значение yбудет найдено немедленно (так как это параметр этой функции), но xне будет ограничено областью действия анонимной функции, поэтому разрешение продолжается в (лексически) верхняя область функции (которая была сохранена в замыкании) гдеx обнаружено, что она связана с1 или5, Теперь мы знаем все для суммирования, поэтому результат можно вернуть, а затем распечатать.

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

Карринг

Да, и вы также узнали, что такое карринг : вы используете функции (замыкания) для передачи каждого аргумента операции вместо использования одной функции с несколькими параметрами.


5

Закрытие - это функция в JavaScript, где функция имеет доступ к своим собственным переменным области видимости, доступ к внешним переменным функции и доступ к глобальным переменным.

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

Внутренняя функция может обращаться к переменным, определенным в ее собственной области видимости, области видимости внешней функции и глобальной области видимости. А внешняя функция может обращаться к переменной, определенной в ее собственной области видимости и глобальной области видимости.

Пример закрытия :

var globalValue = 5;

function functOuter() {
  var outerFunctionValue = 10;

  //Inner function has access to the outer function value
  //and the global variables
  function functInner() {
    var innerFunctionValue = 5;
    alert(globalValue + outerFunctionValue + innerFunctionValue);
  }
  functInner();
}
functOuter();  

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


4

В обычной ситуации переменные связаны правилом видимости: локальные переменные работают только внутри определенной функции. Закрытие является способом временного нарушения этого правила для удобства.

def n_times(a_thing)
  return lambda{|n| a_thing * n}
end

в приведенном выше коде lambda(|n| a_thing * n}это замыкание, потому что a_thingуказывается лямбда (создатель анонимной функции).

Теперь, если вы поместите полученную анонимную функцию в переменную функции.

foo = n_times(4)

foo нарушит нормальное правило области видимости и начнет использовать 4 внутри.

foo.call(3)

возвращается 12.


2

Короче говоря, указатель функции - это просто указатель на местоположение в базе программного кода (например, счетчик программы). Принимая во внимание, что Closure = указатель на функцию + стековый фрейм .

,


1

• Закрытие - это подпрограмма и среда ссылок, в которой она была определена

- Ссылочная среда необходима, если подпрограмма может быть вызвана из любого произвольного места в программе

- Язык со статической областью, который не допускает вложенные подпрограммы, не нуждается в замыканиях

- Замыкания необходимы только в том случае, если подпрограмма может обращаться к переменным во вложенных областях и ее можно вызывать из любого места

- Для поддержки замыканий реализации может потребоваться предоставить неограниченный экстент некоторым переменным (поскольку подпрограмма может обращаться к нелокальной переменной, которая обычно больше не существует)

пример

function makeAdder(x) {
return function(y) {return x + y;}
}
var add10 = makeAdder(10);
var add5 = makeAdder(5);
document.write(″add 10 to 20: ″ + add10(20) +
″<br />″);
document.write(″add 5 to 20: ″ + add5(20) +
″<br />″);

0

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

local old_dofile = dofile

function dofile( filename )
  if filename == nil then
    error( 'Can not use default of stdin.' )
  end

  old_dofile( filename )
end

Значение old_dofile исчезает, когда этот блок кода завершает свою область видимости (потому что он локальный), однако значение было заключено в замыкание, поэтому новая переопределенная функция dofile МОЖЕТ получить к нему доступ, или, скорее, копия, сохраненная вместе с функцией как «повышать стоимость».


0

С Lua.org :

Когда функция написана заключенной в другую функцию, она имеет полный доступ к локальным переменным из включающей функции; эта особенность называется лексической областью видимости. Хотя это может показаться очевидным, это не так. Лексическая область видимости, плюс первоклассные функции, является мощной концепцией в языке программирования, но лишь немногие языки поддерживают эту концепцию.


0

Если вы из мира Java, вы можете сравнить замыкание с функцией-членом класса. Посмотрите на этот пример

var f=function(){
  var a=7;
  var g=function(){
    return a;
  }
  return g;
}

Функция gявляется замыканием: gзакрывается a. Таким образом, gее можно сравнить с функцией-членом, aможно сравнить с полем класса, а функцию - fс классом.


0

Замыкания Всякий раз, когда у нас есть функция, определенная внутри другой функции, внутренняя функция имеет доступ к переменным, объявленным во внешней функции. Замыкания лучше всего объяснить на примерах. В листинге 2-18 видно, что внутренняя функция имеет доступ к переменной (variableInOuterFunction) из внешней области видимости. Переменные во внешней функции были закрыты (или связаны) с внутренней функцией. Отсюда и термин закрытие. Сама концепция достаточно проста и достаточно интуитивна.

Listing 2-18:
    function outerFunction(arg) {
     var variableInOuterFunction = arg;

     function bar() {
             console.log(variableInOuterFunction); // Access a variable from the outer scope
     }
     // Call the local function to demonstrate that it has access to arg
     bar(); 
    }
    outerFunction('hello closure!'); // logs hello closure!

источник: http://index-of.es/Varios/Basarat%20Ali%20Syed%20(auth.)-Beginning%20Node.js-Apress%20(2014).pdf


0

Пожалуйста, посмотрите код ниже, чтобы понять замыкание более глубоко:

        for(var i=0; i< 5; i++){            
            setTimeout(function(){
                console.log(i);
            }, 1000);                        
        }

Вот что будет выходной? 0,1,2,3,4не так будет5,5,5,5,5 из-за закрытия

Так как это решит? Ответ ниже:

       for(var i=0; i< 5; i++){
           (function(j){     //using IIFE           
                setTimeout(function(){
                               console.log(j);
                           },1000);
            })(i);          
        }

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

Вот как это решить, используя IIFE, т.е. выражение функции немедленного вызова

       (function(j){  //i is passed here           
            setTimeout(function(){
                           console.log(j);
                       },1000);
        })(i);  //look here it called immediate that is store i=0 for 1st loop, i=1 for 2nd loop, and so on and print 0,1,2,3,4

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

  • Есть еще одно решение для решения этой проблемы с помощью let (функция ES6), но под капотом работает вышеуказанная функция

     for(let i=0; i< 5; i++){           
         setTimeout(function(){
                        console.log(i);
                    },1000);                        
     }
    
    Output: 0,1,2,3,4
    

=> Больше объяснений:

В памяти, когда для цикла выполнения изображения сделать, как показано ниже:

Петля 1)

     setTimeout(function(){
                    console.log(i);
                },1000);  

Петля 2)

     setTimeout(function(){
                    console.log(i);
                },1000); 

Петля 3)

     setTimeout(function(){
                    console.log(i);
                },1000); 

Петля 4)

     setTimeout(function(){
                    console.log(i);
                },1000); 

Петля 5)

     setTimeout(function(){
                    console.log(i);
                },1000);  

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

поэтому для решения этой проблемы используйте IIFE, как описано выше.


спасибо за Ваш ответ. было бы более читабельным, если бы вы отделили код от объяснения. (не делайте отступы строк, которые не являются кодом)
eMBee

0

Curry: позволяет частично оценить функцию, передав только подмножество ее аргументов. Учти это:

function multiply (x, y) {
  return x * y;
}

const double = multiply.bind(null, 2);

const eight = double(4);

eight == 8;

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

function apple(x){
   function google(y,z) {
    console.log(x*y);
   }
   google(7,2);
}

apple(3);

// the answer here will be 21

0

Закрытие очень просто. Мы можем рассмотреть это следующим образом: Закрытие = функция + его лексическое окружение

Рассмотрим следующую функцию:

function init() {
    var name = “Mozilla”;
}

Каким будет закрытие в вышеуказанном случае? Функция init () и переменные в ее лексической среде, т. Е. Имя. Закрытие = init () + имя

Рассмотрим другую функцию:

function init() {
    var name = “Mozilla”;
    function displayName(){
        alert(name);
}
displayName();
}

Какие здесь будут закрытия? Внутренняя функция может обращаться к переменным внешней функции. displayName () может обращаться к имени переменной, объявленной в родительской функции init (). Однако те же локальные переменные в displayName () будут использоваться, если они существуют.

Закрытие 1: функция инициализации + (имя переменной + функция displayName ()) -> лексическая область

Закрытие 2: функция displayName + (имя переменной) -> лексическая область


0

Замыкания предоставляют JavaScript с состоянием.

Государство в программировании просто означает запоминание вещей.

пример

var a = 0;

a = a + 1; // => 1
a = a + 1; // => 2
a = a + 1; // => 3

В приведенном выше случае состояние сохраняется в переменной «a». Далее мы добавляем 1 к «а» несколько раз. Мы можем сделать это только потому, что можем «запомнить» значение. Держатель состояния «а» хранит это значение в памяти.

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

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

пример

class Bread {
  constructor (weight) {
    this.weight = weight;
  }

  render () {
    return `My weight is ${this.weight}!`;
  }
}

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

Теперь JavaScript является уникальным языком, который исторически не имеет классов (сейчас он есть, но под капотом есть только функции и переменные), поэтому Closures предоставляют возможность JavaScript помнить вещи и обращаться к ним позже.

пример

var n = 0;
var count = function () {
  n = n + 1;
  return n;
};

count(); // # 1
count(); // # 2
count(); // # 3

В приведенном выше примере достигнута цель «сохранить состояние» с помощью переменной. Это замечательно! Однако это имеет тот недостаток, что переменная (держатель "состояния") теперь доступна. Мы можем сделать лучше. Мы можем использовать замыкания.

пример

var countGenerator = function () {
  var n = 0;
  var count = function () {
    n = n + 1;
    return n;
  };

  return count;
};

var count = countGenerator();
count(); // # 1
count(); // # 2
count(); // # 3

Это фантастика.

Теперь наша функция «считать» может считать. Он может сделать это только потому, что может «удерживать» состояние. Состояние в этом случае является переменной «n». Эта переменная сейчас закрыта. Закрыто во времени и пространстве. Со временем, потому что вы никогда не сможете восстановить его, изменить его, присвоить ему значение или напрямую взаимодействовать с ним. В космосе, потому что он географически вложен в функцию countGenerator.

Почему это фантастика? Потому что без привлечения какого-либо другого сложного и сложного инструмента (например, классов, методов, экземпляров и т. Д.) Мы можем: 1. скрыть 2. управление на расстоянии

Мы скрываем состояние, переменную «n», что делает ее закрытой! Мы также создали API, который может управлять этой переменной заданным способом. В частности, мы можем назвать API, например, «count ()», и это добавляет 1 к «n» с «расстояния». Ни при каких обстоятельствах, фигура или форма кого-либо никогда не сможет получить доступ к «n», кроме как через API.

JavaScript действительно удивителен своей простотой.

Закрытия являются большой частью того, почему это так.


0

Простой пример в Groovy для вашей справки:

def outer() {
    def x = 1
    return { -> println(x)} // inner
}
def innerObj = outer()
innerObj() // prints 1
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.