Я поставив запитання про згадування про закриття та закриття. Що таке закриття? Як це стосується каррі?
Я поставив запитання про згадування про закриття та закриття. Що таке закриття? Як це стосується каррі?
Відповіді:
Коли ви оголошуєте локальну змінну, ця змінна має область застосування. Як правило, локальні змінні існують лише в блоці або функції, в якій ви їх оголошуєте.
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
) доступне при виконанні функції лямбда.
Перш за все, всупереч тому, що вам каже більшість людей тут, закриття - це не функція ! Так що це таке?
Це набір символів, визначених у "оточуючому контексті" функції (відомий як її середовище ), який робить його ЗАКРИТИм виразом (тобто виразом, у якому кожен символ визначений і має значення, тому його можна оцінити).
Наприклад, якщо у вас є функція 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
Щоб полегшити розуміння закриттів, може бути корисним вивчити, як вони можуть бути реалізовані процедурною мовою. Це пояснення буде слідувати спрощеній реалізації закриттів у Схемі.
Для початку я повинен ввести поняття простору імен. Коли ви вводите команду в інтерпретатор схеми, вона повинна оцінити різні символи у виразі та отримати їх значення. Приклад:
(define x 3)
(define y 4)
(+ x y) returns 7
Визначення виразів зберігають значення 3 у місці для x та значення 4 у місці для y. Потім, коли ми викликаємо (+ xy), інтерпретатор шукає значення в просторі імен і може виконати операцію та повернути 7.
Однак у схемі є вирази, які дозволяють тимчасово перекрити значення символу. Ось приклад:
(define x 3)
(define y 4)
(let ((x 5))
(+ x y)) returns 9
x returns 3
Що дозволено ключовому слову, це вводить нову область імен з 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. Нарешті, ми називаємо плюс-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?
Ось справжній приклад у світі, чому замикаються дупи ... Це прямо не в моєму коді 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
рамках «S, x
пов'язаний з 1
або 5
( в залежності від закриття).Всередині області анонімної функції x
не пов'язано жодне значення, тому її потрібно вирішити у верхній ( startAt
s) області.
Як говорить Вікіпедія , сфера застосування:
Є область комп'ютерної програми, де прив'язка діє: де ім'я може використовуватися для позначення сутності .
Існує дві методики:
Щоб отримати додаткові пояснення, ознайомтеся з цим питанням і подивіться у Вікіпедію .
У наведеному вище прикладі ми можемо побачити, що JavaScript має лексичний діапазон, тому що, коли x
це вирішено, пошук прив'язки шукається у верхній області ( startAt
s) на основі вихідного коду (анонімна функція, яка шукає x, визначена всередині startAt
) та не ґрунтуючись на стеці викликів, спосіб (область, де) викликалася функція.
У нашому прикладі, коли ми зателефонуємо startAt
, він поверне функцію (першого класу), якій буде призначено closure1
і, closure2
таким чином, буде створено закриття, оскільки передані змінні 1
та 5
будуть збережені в межах startAt
сфери, яка буде додана до повернутого анонімна функція. Коли ми називаємо цю анонімну функцію через closure1
і closure2
з тим самим аргументом ( 3
), значення y
буде знайдено негайно (як це параметр цієї функції), але x
не обмежене в області анонімної функції, тому дозвіл продовжується в (лексично) верхній діапазон функцій (який був збережений у закритті) деx
виявлено, що вона пов'язана з будь-яким 1
або5
. Тепер ми знаємо все для підсумовування, щоб результат можна було повернути, а потім надрукувати.
Тепер ви повинні зрозуміти закриття та те, як вони поводяться, що є основоположною частиною JavaScript.
О, і ви також дізналися, що таке currying : ви використовуєте функції (закриття) для передачі кожного аргументу операції замість того, щоб використовувати одну функцію з кількома параметрами.
Закриття - це функція 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. Мені потрібно було трохи змінити спосіб роботи функції бібліотеки, щоб уникнути проблеми з недоступністю stdin.
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 зникає, коли цей блок коду закінчує область його дії (тому що він локальний), проте значення було вкладено в закриття, тому нова перероблена функція дофіля МОЖЕ отримати доступ до нього, а точніше копія, що зберігається разом із функцією як 'цінність'.
З Lua.org :
Коли функція записується укладеній в іншій функції, вона має повний доступ до локальних змінних із функції, що додає; ця особливість називається лексичним обстеженням. Хоча це може здатися очевидним, це не так. Лексичне оцінювання, а також першокласні функції - це потужне поняття в мові програмування, але мало хто підтримує цю концепцію.
Якщо ви зі світу Java, ви можете порівняти закриття з функцією-членом класу. Подивіться на цей приклад
var f=function(){
var a=7;
var g=function(){
return a;
}
return g;
}
Функція g
- це закриття: g
закривається a
. Так g
можна порівняти з функцією-членом, a
можна порівняти з полем класу, а функцію - f
з класом.
Закриття Кожен раз, коли у нас є функція, визначена всередині іншої функції, внутрішня функція має доступ до змінних, оголошених у зовнішній функції. Закриття найкраще пояснити на прикладах. У Лістингу 2-18 ви бачите, що внутрішня функція має доступ до змінної (змінноїInOuterFunction) із зовнішньої області. Змінні у зовнішній функції були закриті (або пов'язані) внутрішньою функцією. Звідси термін закриття. Сама по собі концепція досить проста і досить інтуїтивна.
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);
}
Дозвольте мені просто пояснити, коли створена функція нічого не відбувається, поки вона не викликала так для циклу в 1-му коді, який називається 5 разів, але не викликається негайно, коли він викликається, тобто через 1 секунду, а також це асинхронно, перш ніж це для циклу закінчено і збереже значення 5 в var i і, нарешті, виконайте 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);
Тут я не виконується, і тоді після завершення циклу, var я зберігає значення 5 в пам'яті, але його область завжди видна в його дитячій функції, тому при виконанні функції всередині setTimeout
п’ять разів вона друкується5,5,5,5,5
щоб вирішити це використання IIFE, як пояснено вище.
Currying: Це дозволяє частково оцінити функцію, лише передавши підмножину її аргументів. Врахуйте це:
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: функція init + (змінна назва + функція 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}!`;
}
}
Як ми можемо отримати доступ до "ваги" в межах методу "візуалізації"? Ну, дякую державі. Кожен екземпляр Хліба класу може надати власну вагу, читаючи його з "стану", місця в пам'яті, де ми могли б зберігати цю інформацію.
Тепер JavaScript - це дуже унікальна мова якій історично немає класів (зараз це є, але під кришкою є лише функції та змінні), тому закриття - це спосіб 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
Тепер наша функція "count" може рахувати. Це вдається лише тому, що він може "утримувати" стан. Стан у цьому випадку є змінною "n". Ця змінна зараз закрита. Закритий у часі та просторі. Вчасно, тому що ви ніколи не зможете відновити його, змінити його, призначити йому значення або взаємодіяти безпосередньо з ним. У просторі, оскільки він географічно вкладений у функцію "countGenerator".
Чому це фантастично? Тому що без залучення будь-якого іншого складного та складного інструменту (наприклад, класів, методів, примірників тощо) ми можемо 1. приховати 2. контролювати відстань
Ми приховуємо стан, змінну "n", що робить його приватною змінною! Ми також створили API, який може керувати цією змінною заздалегідь визначеним способом. Зокрема, ми можемо назвати API на зразок "count ()", який додає 1 до "n" з "відстані". Ні в якому разі, форма чи форма ніколи не зможуть отримати доступ до "n", окрім API.
Закриття є важливою частиною того, чому це відбувається.