Я задал вопрос о карринге и были упомянуты закрытия. Что такое закрытие? Как это связано с карри?
Я задал вопрос о карринге и были упомянуты закрытия. Что такое закрытие? Как это связано с карри?
Ответы:
Когда вы объявляете локальную переменную, эта переменная имеет область видимости. Как правило, локальные переменные существуют только внутри блока или функции, в которой вы их объявляете.
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
находится в закрытии.
Я приведу пример (в 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, он знает, где найти значение для выполнения сложения.
Ответ Кайла довольно хорош. Я думаю, что единственным дополнительным разъяснением является то, что замыкание - это в основном снимок стека в момент создания лямбда-функции. Затем при повторном выполнении функции стек восстанавливается до этого состояния перед выполнением функции. Таким образом, как упоминает Кайл, это скрытое значение ( count
) доступно при выполнении лямбда-функции.
Прежде всего, вопреки тому, что большинство людей здесь говорят вам, закрытие не является функцией ! И что же это?
Это набор символов, определенных в «окружающем контексте» функции (известный как ее окружение ), которые делают его выражением 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
быть там, когда она вызывается! Другими словами, требуется, чтобы переменные из своего замыкания как-то пережили функцию-обертку и были там, когда это необходимо. Следовательно, внутренняя функция должна сделать снимок этих переменных, которые закрывают их, и хранить их где-нибудь в безопасности для дальнейшего использования. (Где-то за пределами стека вызовов.)
И именно поэтому люди часто путают термин « закрытие» с тем, что это особый тип функции, который может делать такие снимки внешних переменных, которые они используют, или структуры данных, используемой для хранения этих переменных для дальнейшего использования. Но я надеюсь, что теперь вы понимаете, что это не само замыкание - это просто способы реализации замыканий в языке программирования или языковые механизмы, которые позволяют переменным из замыкания функции быть там, когда это необходимо. Есть много заблуждений относительно замыканий, которые (излишне) делают этот предмет намного более запутанным и сложным, чем он есть на самом деле.
Закрытие - это функция, которая может ссылаться на состояние в другой функции. Например, в 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
Чтобы облегчить понимание замыканий, может быть полезно изучить, как они могут быть реализованы на процедурном языке. Это объяснение будет следовать упрощенной реализации замыканий в схеме.
Для начала я должен представить концепцию пространства имен. Когда вы вводите команду в интерпретатор 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?
Вот реальный пример того, почему 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
с помощью 5000
ms, запускается первый фрагмент и сохраняет переданные аргументы в его замыкании. Затем, через 5 секунд, когда setTimeout
происходит обратный вызов, замыкание все еще поддерживает эти переменные, поэтому он может вызывать исходную функцию с исходными параметрами.
Это тип карри или функционального оформления.
Без замыканий вам пришлось бы каким-то образом поддерживать состояние этих переменных вне функции, таким образом засоряя код вне функции чем-то, что логически принадлежит ей. Использование замыканий может значительно улучшить качество и удобочитаемость вашего кода.
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
}
Замыкание - это функция, и ее область действия назначается (или используется как) переменной. Таким образом, закрытие имени: область действия и функция заключены и используются так же, как и любая другая сущность.
Согласно Википедии, закрытие :
Методы реализации привязки лексически ограниченных имен в языках с первоклассными функциями.
Что это обозначает? Давайте посмотрим на некоторые определения.
Я объясню замыкания и другие связанные определения, используя этот пример:
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.
Да, и вы также узнали, что такое карринг : вы используете функции (замыкания) для передачи каждого аргумента операции вместо использования одной функции с несколькими параметрами.
Закрытие - это функция в 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, которые представляют собой сумму его собственной внутренней переменной, переменной внешней функции и значения глобальной переменной.
В обычной ситуации переменные связаны правилом видимости: локальные переменные работают только внутри определенной функции. Закрытие является способом временного нарушения этого правила для удобства.
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.
• Закрытие - это подпрограмма и среда ссылок, в которой она была определена
- Ссылочная среда необходима, если подпрограмма может быть вызвана из любого произвольного места в программе
- Язык со статической областью, который не допускает вложенные подпрограммы, не нуждается в замыканиях
- Замыкания необходимы только в том случае, если подпрограмма может обращаться к переменным во вложенных областях и ее можно вызывать из любого места
- Для поддержки замыканий реализации может потребоваться предоставить неограниченный экстент некоторым переменным (поскольку подпрограмма может обращаться к нелокальной переменной, которая обычно больше не существует)
пример
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 />″);
Вот еще один реальный пример из жизни и использование популярного в играх языка сценариев - 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 МОЖЕТ получить к нему доступ, или, скорее, копия, сохраненная вместе с функцией как «повышать стоимость».
С Lua.org :
Когда функция написана заключенной в другую функцию, она имеет полный доступ к локальным переменным из включающей функции; эта особенность называется лексической областью видимости. Хотя это может показаться очевидным, это не так. Лексическая область видимости, плюс первоклассные функции, является мощной концепцией в языке программирования, но лишь немногие языки поддерживают эту концепцию.
Если вы из мира Java, вы можете сравнить замыкание с функцией-членом класса. Посмотрите на этот пример
var f=function(){
var a=7;
var g=function(){
return a;
}
return g;
}
Функция g
является замыканием: g
закрывается a
. Таким образом, g
ее можно сравнить с функцией-членом, a
можно сравнить с полем класса, а функцию - f
с классом.
Замыкания Всякий раз, когда у нас есть функция, определенная внутри другой функции, внутренняя функция имеет доступ к переменным, объявленным во внешней функции. Замыкания лучше всего объяснить на примерах. В листинге 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
Пожалуйста, посмотрите код ниже, чтобы понять замыкание более глубоко:
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, как описано выше.
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
Закрытие очень просто. Мы можем рассмотреть это следующим образом: Закрытие = функция + его лексическое окружение
Рассмотрим следующую функцию:
function init() {
var name = “Mozilla”;
}
Каким будет закрытие в вышеуказанном случае? Функция init () и переменные в ее лексической среде, т. Е. Имя. Закрытие = init () + имя
Рассмотрим другую функцию:
function init() {
var name = “Mozilla”;
function displayName(){
alert(name);
}
displayName();
}
Какие здесь будут закрытия? Внутренняя функция может обращаться к переменным внешней функции. displayName () может обращаться к имени переменной, объявленной в родительской функции init (). Однако те же локальные переменные в displayName () будут использоваться, если они существуют.
Закрытие 1: функция инициализации + (имя переменной + функция displayName ()) -> лексическая область
Закрытие 2: функция displayName + (имя переменной) -> лексическая область
Государство в программировании просто означает запоминание вещей.
пример
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.
Закрытия являются большой частью того, почему это так.