Замыкание что такое: Замыкания в JavaScript для начинающих | by Nikita | WebbDEV
Замыкания в JavaScript для начинающих | by Nikita | WebbDEV
Замыкания — это одна из фундаментальных концепций JavaScript, вызывающая сложности у многих новичков, знать и понимать которую должен каждый JS-программист. Хорошо разобравшись с замыканиями, вы сможете писать более качественный, эффективный и чистый код. А это, в свою очередь, будет способствовать вашему профессиональному росту.
Материал, перевод которого мы публикуем сегодня, посвящён рассказу о внутренних механизмах замыканий и о том, как они работают в JavaScript-программах.
Замыкание — это функция, у которой есть доступ к области видимости, сформированной внешней по отношению к ней функции даже после того, как эта внешняя функция завершила работу. Это значит, что в замыкании могут храниться переменные, объявленные во внешней функции и переданные ей аргументы. Прежде чем мы перейдём, собственно, к замыканиям, разберёмся с понятием «лексическое окружение».
Понятие «лексическое окружение» или «статическое окружение» в JavaScript относится к возможности доступа к переменным, функциям и объектам на основе их физического расположения в исходном коде. Рассмотрим пример:
Здесь у функции inner()
есть доступ к переменным, объявленным в её собственной области видимости, в области видимости функции outer()
и в глобальной области видимости. Функция outer()
имеет доступ к переменным, объявленным в её собственной области видимости и в глобальной области видимости.
Цепочка областей видимости вышеприведённого кода будет выглядеть так:
Обратите внимание на то, что функция inner()
окружена лексическим окружением функции outer()
, которая, в свою очередь, окружена глобальной областью видимости. Именно поэтому функция inner()
может получить доступ к переменным, объявленным в функции outer()
и в глобальной области видимости.
Рассмотрим, прежде чем разбирать тонкости внутреннего устройства замыканий, несколько практических примеров.
Пример №1
Здесь мы вызываем функцию person()
, которая возвращает внутреннюю функцию displayName()
, и сохраняем эту функцию в переменной peter
. Когда мы, после этого, вызываем функцию
peter()
(соответствующая переменная, на самом деле, хранит ссылку на функцию displayName()
), в консоль выводится имя Peter
.
При этом в функции displayName()
нет переменной с именем name
, поэтому мы можем сделать вывод о том, что эта функция может каким-то образом получать доступ к переменной, объявленной во внешней по отношению к ней функции, person()
, даже после того, как эта функция отработала. Возможно это так из-за того, что функция displayName()
, на самом деле, является замыканием.
Пример №2
Тут, как и в предыдущем примере, мы храним ссылку на анонимную внутреннюю функцию, возвращённую функцией getCounter()
, в переменной count
. Так как функция count()
представляет собой замыкание, она может обращаться к переменной counter
функции getCount()
даже после того, как функция getCounter()
завершила работу.
Обратите внимание на то, что значение переменной counter
не сбрасывается в 0 при каждом вызове функции count()
. Может показаться, что оно должно сбрасываться в 0, как могло бы быть при вызове обычной функции, но этого не происходит.
Всё работает именно так из-за того, что при каждом вызове функции count()
для неё создаётся новая область видимости, но существует лишь одна область видимости для функции getCounter()
. Так как переменная counter
объявлена в области видимости функции getCounter()
, её значение между вызовами функции count()
сохраняется, не сбрасываясь в 0.
До сих пор мы говорили о том, что такое замыкания, и рассматривали практические примеры. Теперь поговорим о внутренних механизмах JavaScript, обеспечивающих их работу.
Для того чтобы понять замыкания, нам нужно разобраться с двумя важнейшими концепциями JavaScript. Это — контекст выполнения (Execution Context) и лексическое окружение (Lexical Environment).
Контекст выполнения
Контекст выполнения — это абстрактное окружение, в котором вычисляется и выполняется JavaScript-код. Когда выполняется глобальный код, это происходит внутри глобального контекста выполнения. Код функции выполняется внутри контекста выполнения функции.
В некий момент времени может выполняться код лишь в одном контексте выполнения (JavaScript — однопоточный язык программирования). Управление этими процессами ведётся с использованием так называемого стека вызовов (Call Stack).
Стек вызовов — это структура данных, устроенная по принципу LIFO (Last In, First Out — последним вошёл, первым вышел). Новые элементы можно помещать только в верхнюю часть стека, и только из неё же элементы можно изымать.
Текущий контекст выполнения всегда будет в верхней части стека, и когда текущая функция завершает работу, её контекст выполнения извлекается из стека и управление передаётся контексту выполнения, который был расположен ниже контекста этой функции в стеке вызовов.
Рассмотрим следующий пример для того, чтобы лучше разобраться в том, что такое контекст выполнения и стек вызовов:
Пример контекста выполненияКогда выполняется этот код, JavaScript-движок создаёт глобальный контекст выполнения для выполнения глобального кода, а когда встречает вызов функции first()
, создаёт новый контекст выполнения для этой функции и помещает его в верхнюю часть стека.
Стек вызовов этого кода выглядит так:
Стек вызововКогда завершается выполнение функции first()
, её контекст выполнения извлекается из стека вызовов и управление передаётся контексту выполнения, находящемуся ниже его, то есть — глобальному контексту. После этого будет выполнен оставшийся в глобальной области видимости код.
Лексическое окружение
Каждый раз, когда JS-движок создаёт контекст выполнения для выполнения функции или глобального кода, он создаёт и новое лексическое окружение для хранения переменных, объявляемых в этой функции в процессе её выполнения.
Лексическое окружение — это структура данных, которая хранит сведения о соответствии идентификаторов и переменных. Здесь «идентификатор» — это имя переменной или функции, а «переменная» — это ссылка на объект (сюда входят и функции) или значение примитивного типа.
Лексическое окружение содержит два компонента:
- Запись окружения (environment record) — место, где хранятся объявления переменных и функций.
- Ссылка на внешнее окружение (reference to the outer environment) — ссылка, позволяющая обращаться к внешнему (родительскому) лексическому окружению. Это — самый важный компонент, с которым нужно разобраться для того, чтобы понять замыкания.
Концептуально лексическое окружение выглядит так:
Взглянем на следующий фрагмент кода:
Когда JS-движок создаёт глобальный контекст выполнения для выполнения глобального кода, он создаёт и новое лексическое окружение для хранения переменных и функций, объявленных в глобальной области видимости. В результате лексическое окружение глобальной области видимости будет выглядеть так:\
Обратите внимание на то, что ссылка на внешнее лексическое окружение (outer
) установлена в значение null
, так как у глобальной области видимости нет внешнего лексического окружения.
Когда движок создаёт контекст выполнения для функции first()
, он создаёт и лексическое окружение для хранения переменных, объявленных в этой функции в ходе её выполнения. В результате лексическое окружение функции будет выглядеть так:
Ссылка на внешнее лексическое окружение функции установлена в значение <globalLexicalEnvironment>
, так как в исходном коде код функции находится в глобальной области видимости.
Обратите внимание на то, что когда функция завершит работу, её контекст выполнения извлекается из стека вызовов, но её лексическое окружение может быть удалено из памяти, а может и остаться там. Это зависит от того, существуют ли в других лексических окружениях ссылки на данное лексическое окружение в виде ссылок на внешнее лексическое окружение.
Теперь, когда мы вооружились знаниями о контексте выполнения и о лексическом окружении, вернёмся к замыканиям и более глубоко проанализируем те же фрагменты кода, которые мы уже рассматривали.
Пример №1
Взгляните на данный фрагмент кода:
Когда выполняется функция person()
, JS-движок создаёт новый контекст выполнения и новое лексическое окружение для этой функции. Завершая работу, функция возвращает функцию displayName()
, в переменную peter
записывается ссылка на эту функцию.
Её лексическое окружение будет выглядеть так:
Когда функция person()
завершает работу, её контекст выполнения извлекается из стека. Но её лексическое окружение остаётся в памяти, так как ссылка на него есть в лексическом окружении её внутренней функции displayName()
. В результате переменные, объявленные в этом лексическом окружении, остаются доступными.
Когда вызывается функция peter()
(соответствующая переменная хранит ссылку на функцию displayName()
), JS-движок создаёт для этой функции новый контекст выполнения и новое лексическое окружение. Это лексическое окружение будет выглядеть так:
В функции displayName()
нет переменных, поэтому её запись окружения будет пустой. В процессе выполнения этой функции JS-движок попытается найти переменную
name
в лексическом окружении функции.
Так как в лексическом окружении функции displayName()
искомое найти не удаётся, поиск продолжится во внешнем лексическом окружении, то есть, в лексическом окружении функции person()
, которое всё ещё находится в памяти. Там движок находит нужную переменную и выводит её значение в консоль.
Пример №2
Лексическое окружение функции getCounter()
будет выглядеть так:
Эта функция возвращает анонимную функцию, которая назначается переменной count
.
Когда выполняется функция count()
, её лексическое окружение выглядит так:
При выполнении этой функции система будет искать переменную counter
в её лексическом окружении. В данном случае, опять же, запись окружения функции пуста, поэтому поиск переменной продолжается во внешнем лексическом окружении функции.
Движок находит переменную, выводит её в консоль и инкрементирует переменную counter
, хранящуюся в лексическом окружении функции getCounter()
.
В результате лексическое окружение функции getCounter()
после первого вызова функции count()
будет выглядеть так:
При каждом следующем вызове функции count()
JavaScript-движок создаёт новое лексическое окружение для этой функции и инкрементирует переменную counter
, что приводит к изменениям в лексическом окружении функции getCounter()
.
В этом материале мы поговорили о том, что такое замыкания, и разобрали глубинные механизмы JavaScript, лежащие в их основе. Замыкания — одна из важнейших фундаментальных концепций JavaScript, её должен понимать каждый JS-разработчик. Понимание замыканий — это одна из ступеней пути к написанию эффективных и качественных приложений.
Перевод статьи Understanding Closures in JavaScript
Замыкания — JavaScript | MDN
Замыкание — это комбинация функции и лексического окружения, в котором эта функция была определена. Другими словами, замыкание дает вам доступ к Scope внешней функции из внутренней функции. В JavaScript замыкания создаются каждый раз при создании функции, во время ее создания.
Рассмотрим следующий пример:
function init() {
var name = "Mozilla";
function displayName() {
alert (name);
}
displayName();
}
init();
init()
создаёт локальную переменную name
и определяет функцию displayName()
. displayName()
— это внутренняя функция — она определена внутри init()
и доступна только внутри тела функции init()
. Обратите внимание, что функция displayName()
не имеет никаких собственных локальных переменных. Однако, поскольку внутренние функции имеют доступ к переменным внешних функций,
может иметь доступ к переменной name
, объявленной в родительской функции init()
.
Выполните этот код и обратите внимание, что команда alert()
внутри displayName()
благополучно выводит на экран содержимое переменной name
объявленной в родительской функции. Это пример так называемой лексической области видимости (lexical scoping): в JavaScript область действия переменной определяется по её расположению в коде (это очевидно
Рассмотрим следующий пример:
function makeFunc() {
var name = "Mozilla";
function displayName() {
alert(name);
}
return displayName;
};
var myFunc = makeFunc();
myFunc();
Если выполнить этот код, то результат будет такой же, как и выполнение init()
из предыдущего примера: строка «Mozilla» будет показана в JavaScript alert диалоге. Что отличает этот код и представляет для нас интерес, так это то, что внутренняя функция displayName()
была возвращена из внешней до того, как была выполнена.
На первый взгляд, кажется неочевидным, что этот код правильный, но он работает. В некоторых языках программирования локальные переменные-функции существуют только во время выполнения этой функции. После завершения выполнения
makeFunc()
можно ожидать, что переменная name больше не будет доступна. Однако, поскольку код продолжает нормально работать, очевидно, что это не так в случае JavaScript.
Причина в том, что функции в JavaScript формируют так называемые замыкания. Замыкание
myFunc
— это ссылка на экземпляр функции displayName
, созданной в результате выполнения makeFunc
. Экземпляр функции displayName
в свою очередь сохраняет ссылку на своё лексическое окружение, в котором есть переменная name
. По этой причине, когда происходит вызов функции myFunc
, переменная name
остаётся доступной для использования и сохраненный в ней текст «Mozilla» передаётся в
alert
.
А вот немного более интересный пример — функция makeAdder
:
function makeAdder(x) {
return function(y) {
return x + y;
};
};
var add5 = makeAdder(5);
var add10 = makeAdder(10);
console.log(add5(2));
console.log(add10(2));
Здесь мы определили функцию makeAdder(x)
, которая получает единственный аргумент x
и возвращает новую функцию. Эта функция получает единственный аргумент y
и возвращает сумму x
и y
.
По существу makeAdder
— это фабрика функций: она создает функции, которые могут прибавлять определённое значение к своему аргументу. В примере выше мы используем нашу фабричную функцию для создания двух новых функций — одна прибавляет 5 к своему аргументу, вторая прибавляет 10.
add5
и add10
— это примеры замыканий. Эти функции делят одно определение тела функции, но при этом они сохраняют различные окружения. В окружении функции
add5
x
— это 5, в то время как в окружении add10
x
— это 10.
Замыкания полезны тем, что позволяют связать данные (лексическое окружение) с функцией, которая работает с этими данными. Очевидна параллель с объектно-ориентированным программированием, где объекты позволяют нам связать некоторые данные (свойства объекта) с одним или несколькими методами.
Следовательно, замыкания можно использовать везде, где вы обычно использовали объект с одним единственным методом.
Такие ситуации повсеместно встречаются в web-разработке. Большое количество front-end кода, который мы пишем на JavaScript, основанно на обработке событий. Мы описываем какое-то поведение, а потом связываем его с событием, которое создается пользователем (например, клик мышкой или нажатие клавиши). При этом наш код обычно привязывается к событию в виде обратного/ответного вызова (callback):
Давайте рассмотрим практический пример: допустим, мы хотим добавить на страницу несколько кнопок, которые будут менять размер текста. Как вариант, мы можем указать свойство font-size на элементе body в пикселах, а затем устанавливать размер прочих элементов страницы (таких, как заголовки) с использованием относительных единиц em:
body { font-family: Helvetica, Arial, sans-serif; font-size: 12px; } h2 { font-size: 1.5em; } h3 { font-size: 1.2em; }
Тогда наши кнопки будут менять свойство font-size элемента body, а остальные элементы страницы просто получат это новое значение и отмасштабируют размер текста благодаря использованию относительных единиц.
Используем следующий JavaScript:
function makeSizer(size) {
return function() {
document.body.style.fontSize = size + 'px';
};
};
var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);
Теперь size12
, size14
, и size16
— это функции, которые меняют размер текста в элементе body на значения 12, 14, и 16 пикселов, соответственно.
document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;
<a href="#">12</a>
<a href="#">14</a>
<a href="#">16</a>
Языки вроде Java позволяют нам объявлять частные (private) методы . Это значит, что они могут быть вызваны только методами того же класса, в котором объявлены.
JavaScript не имеет встроенной возможности сделать такое, но это можно эмулировать с помощью замыкания. Частные методы полезны не только тем, что ограничивают доступ к коду, это также мощное средство глобальной организации пространства имен, позволяющее не засорять публичный интерфейс вашего кода внутренними методами классов.
Код ниже иллюстрирует, как можно использовать замыкания для определения публичных функций, которые имеют доступ к закрытым от пользователя (private) функциям и переменным. Такая манера программирования называется модульное программирование:
var Counter = (function() { var privateCounter = 0; function changeBy(val) { privateCounter += val; } return { increment: function() { changeBy(1); }, decrement: function() { changeBy(-1); }, value: function() { return privateCounter; } }; })(); alert(Counter.value()); Counter.increment(); Counter.increment(); alert(Counter.value()); Counter.decrement(); alert(Counter.value());
Тут много чего поменялось. В предыдущем примере каждое замыкание имело свой собственный контекст исполнения (окружение). Здесь мы создаем единое окружение для трех функций: Counter.increment
, Counter.decrement
, и Counter.value
.
Единое окружение создается в теле анонимной функции, которая исполняется в момент описания. Это окружение содержит два приватных элемента: переменную privateCounter
changeBy(val)
.
Эти три публичные функции являются замыканиями, использующими общий контекст исполнения (окружение). Благодаря механизму lexical scoping в Javascript, все они имеют доступ к переменной privateCounter
и функции changeBy
.
Заметьте, мы описываем анонимную фунцию, создающую счётчик, и тут же запускаем ее, присваивая результат исполнения переменной Counter
. Но мы также можем не запускать эту функцию сразу, а сохранить её в отдельной переменной, чтобы использовать для дальнейшего создания нескольких счётчиков вот так:
var makeCounter = function() {
var privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return privateCounter;
}
}
};
var Counter1 = makeCounter();
var Counter2 = makeCounter();
alert(Counter1.
value());
Counter1.increment();
Counter1.increment();
alert(Counter1.value());
Counter1.decrement();
alert(Counter1.value());
alert(Counter2.value());
Заметьте, что счетчики работают независимо друг от друга. Это происходит потому, что у каждого из них в момент создания функцией makeCounter()
также создавался свой отдельный контекст исполнения (окружение). То есть приватная переменная privateCounter
в каждом из счетчиков это действительно отдельная, самостоятельная переменная.
Используя замыкания подобным образом, вы получаете ряд преимуществ, обычно ассоциируемых с объектно-ориентированным программированием, таких как изоляция и инкапсуляция.
До того, как в версии ECMAScript 6 ввели ключевое слово let
, постоянно возникала следующая проблема при создании замыканий внутри цикла. Рассмотрим пример:
<p>Helpful notes will appear here</p>
<p>E-mail: <input type="text" name="email"></p>
<p>Name: <input type="text" name="name"></p>
<p>Age: <input type="text" name="age"></p>
function showHelp(help) {
document.
getElementById('help').innerHTML = help;
}
function setupHelp() {
var helpText = [
{'id': 'email', 'help': 'Ваш адрес e-mail'},
{'id': 'name', 'help': 'Ваше полное имя'},
{'id': 'age', 'help': 'Ваш возраст (Вам должно быть больше 16)'}
];
for (var i = 0; i < helpText.length; i++) {
var item = helpText[i];
document.getElementById(item.id).onfocus = function() {
showHelp(item.help);
}
}
}
setupHelp();
Массив helpText
описывает три подсказки для трех полей ввода. Цикл пробегает эти описания по очереди и для каждого из полей ввода определяет, что при возникновении события onfocus
для этого элемента должна вызываться функция, показывающая соответствующую подсказку.
Если вы запустите этот код, то увидите, что он работает не так, как мы ожидаем интуитивно. Какое поле вы бы ни выбрали, в качестве подсказки всегда будет высвечиваться сообщение о возрасте.
Проблема в том, что функции, присвоенные как обработчики события onfocus
, являются замыканиями. Они состоят из описания функции и контекста исполнения (окружения), унаследованного от функции
setupHelp
. Было создано три замыкания, но все они были созданы с одним и тем же контекстом исполнения. К моменту возникновения события onfocus
цикл уже давно отработал, а значит, переменная item
(одна и та же для всех трех замыканий) указывает на последний элемент массива, который как раз в поле возраста.
В качестве решения в этом случае можно предложить использование функции, фабричной функции (function factory), как уже было описано выше в примерах:
function showHelp(help) {
document.getElementById('help').innerHTML = help;
}
function makeHelpCallback(help) {
return function() {
showHelp(help);
};
}
function setupHelp() {
var helpText = [
{'id': 'email', 'help': 'Ваш адрес e-mail'},
{'id': 'name', 'help': 'Ваше полное имя'},
{'id': 'age', 'help': 'Ваш возраст (Вам должно быть больше 16)'}
];
for (var i = 0; i < helpText.
length; i++) {
var item = helpText[i];
document.getElementById(item.id).onfocus = makeHelpCallback(item.help);
}
}
setupHelp();
Вот это работает как следует. Вместо того, чтобы делить на всех одно окружение, функция makeHelpCallback
создает каждому из замыканий свое собственное, в котором переменная item
указывает на правильный элемент массива helpText
.
Не нужно без необходимости создавать функции внутри функций в тех случаях, когда замыкания не нужны. Использование этой техники увеличивает требования к производительности как в части скорости, так и в части потребления памяти.
Как пример, при написании нового класса есть смысл помещать все методы в прототип его объекта, а не описывать их в тексте конструктора. Если сделать по-другому, то при каждом создании объекта для него будет создан свой экземпляр каждого из методов, вместо того, чтобы наследовать их из прототипа.
Давайте рассмотрим не очень практичный, но показательный пример:
function MyObject(name, message) {
this.
name = name.toString();
this.message = message.toString();
this.getName = function() {
return this.name;
};
this.getMessage = function() {
return this.message;
};
}
Поскольку вышеприведенный код никак не использует преимущества замыканий, его можно переписать следующим образом:
function MyObject(name, message) {
this.name = name.toString();
this.message = message.toString();
}
MyObject.prototype = {
getName: function() {
return this.name;
},
getMessage: function() {
return this.message;
}
};
Методы вынесены в прототип. Тем не менее, переопределять прототип — само по себе является плохой привычкой, поэтому давайте перепишем всё так, чтобы новые методы просто добавились к уже существующему прототипу.
function MyObject(name, message) {
this.name = name.toString();
this.message = message.toString();
}
MyObject.prototype.getName = function() {
return this.name;
};
MyObject.prototype.
getMessage = function() {
return this.message;
};
Код выше можно сделать аккуратнее:
function MyObject(name, message) {
this.name = name.toString();
this.message = message.toString();
}
(function() {
this.getName = function() {
return this.name;
};
this.getMessage = function() {
return this.message;
};
}).call(MyObject.prototype);
В обоих примерах выше методы определяются один раз — в прототипе. И все объекты, использующие данный прототип, будут использовать это определение без дополнительного расхода вычислительных ресурсов. Смотрите подробное описание в статье Подробнее об объектной модели.
Что такое короткое замыкание по-простому
КОРОТКОЕ ЗАМЫКАНИЕ – это электрическое соединение разных фаз или потенциалов электроустановки между собой или с землей, не предусмотренное в нормальном режиме работы, при котором в проводниках, в месте контакта, резко возрастает сила тока, превышая максимально допустимые величины.
Если же говорить простым языком, короткое замыкание – это любое незапланированное, нештатное соединение электрических проводников с разным потенциалом, например, фазы и ноля, при котором образуются разрушительные токи.
Как вы заметили, акцент на том, что короткое замыкание в электрической цепи – это именно незапланированный, не предусмотренный процесс, сделан не зря, ведь, по большому счету, контролируемое замыкание (некоторые еще назывыают его по-аналогии длинным) запускает электроприборы. Все они включаются в розетку, и, так или иначе, фазный провод, посредством электроприбора соединяется с нулевым, но короткого замыкания при этом не происходит, давайте разберемся почему.
Почему происходит короткое замыкание
Для того чтобы понять почему происходит короткое замыкание, нужно вспомнить закон Ома для участка цепи – «Сила тока в участке цепи прямо пропорциональна напряжению и обратно пропорциональна электрическому сопротивлению на этом участке», формула при этом следующая:
I=U/R
где I – сила тока, U – напряжение на участке цепи, R – сопротивление.
Любой электроприбор в квартире, включающийся в розетку, это активное сопротивление (R – в формуле), напряжение в бытовой электросети вам должно быть известно – 220В-230 В и оно практически не меняется. Соответственно, чем выше сопротивление электроприбора (или материала, проводника и т.д.) включаемого в сеть, тем меньше величина тока, так, как зависимость между этими величинами обратно пропорциональная.
Теперь представьте, что мы включаем в сеть электроприбор практически без сопротивления, допустим его величина R=0.05 Ом, считаем, что тогда будет с силой тока по закону Ома.
I=220В(U)/0,05(Ом)=4400А
В результате получается очень высокий ток, для сравнения стандартная электрическая розетка в нашей квартире, выдерживает лишь ток 10-16А, а у нас по расчетам 4,4 кА.
Современные медные провода, используемые в проводке, имеют настолько хорошие показатели электрической проводимости, что их сопротивление, при относительно небольшой длине, можно принять за ноль. Соответственно, прямое соединение фазного и нулевого провода, можно сравнить, с подключением к сети электроприбора, с очень низким сопротивлением. Чаще всего, в бытовых условиях, мы сталкиваемся именно с таким типом короткого замыкания.
Конечно, это очень грубый пример, в реальных условиях, при расчете силы тока при коротком замыкании, учитывать приходится гораздо больше показателей, таких как: сопротивление всей линии проводов, идущих к вам, соединений, дополнительного оборудования сети и даже дуги образующейся при коротком замыкании, а также некоторых других.Поэтому, чаще всего, сопротивление будет выше тех 0,05 Ом, что мы взяли в расчете, но общий принцип возникновения КЗ и его разрушительных эффектов понятен.
Почему короткое замыкание так называется
Подключая какую-то нагрузку к сети, например, утюг, телевизор или любой другой электроприбор, мы создаём сопротивление для протекания электрического тока.
Если же мы умышленно или случайно соединим, например, фазу и ноль напрямую, без нагрузки, мы, в каком-то смысле, укорачиваем путь, делаем его коротким.
Поэтому, короткое замыкание и называют коротким, подразумевая движение электронов по кротчайшему пути, без сопротивления.
Чем опасно короткое замыкание
Самая значительная опасность при коротком замыкании – это большая вероятность возникновения пожара.
При значительном увеличении силы тока, которое происходит при КЗ, выделяется большое количество теплоты в проводниках, что вызывает разрушение изоляции и возгорание.
Кроме того, в быту, чаще всего происходит дуговое короткое замыкание, при котором, между проводниками в месте КЗ, возникает мощнейший электрический разряд, который нередко воспламеняет окружающие предметы.
Так же не стоит забывать про опасность поражения электрическим током или резким выделением тепла человека, которая так же достаточно высока.
Из менее опасных последствий, происходящих при КЗ, стоит отменить значительное снижение напряжения в электрической сети особенно в месте его возникновения, что негативно влияет на различные электроприборы, в частности оснащенные двигателями. Также, не стоит забывать про сильное электромагнитное воздействие на чувствительное к этому оборудование.
Как видите, последствия от возникновения короткого замыкания могут быть очень серьезными, поэтому, при проектировании любой электроустановки и монтаже электропроводки, необходимо предусмотреть защиту от короткого замыкания.
Защита от короткого замыкания
Большинство современных способов защиты от короткого замыкания основаны на принципе разрыва электрической цепи, при обнаружении КЗ.
Самые простые устройства, которые есть во многих электроприборах, защищающие от последствий коротких замыканий – это плавкие предохранители.
Чаще всего, плавкий предохранитель представляет собой проводник, рассчитанный на определенный предельный ток, который он сможет пропускать через себя, при превышении этого значения, проводник разрушается, тем самым разрывая электрическую цепь. Плавкий предохранитель – это самый слабый участок электрической цепи, который первый выходит из строя под действием высокого тока, тем самым защищает все остальные элементы.
Для защиты от коротких замыканий в квартире или доме, используются автоматические выключатели -АВ (чаще всего их называют просто автоматы), они устанавливаются на каждую группу электрической сети.
Каждый автоматический выключатель рассчитан на определенный рабочий ток, при превышении которого он разрывает цепь. Это происходит либо с помощью теплового расцепителя, который при нагреве, вследствие протекания высокого тока, механически разъединяет контакты, либо с помощью электромагнитного.
Принцип работы автоматических выключателей — это тема отдельной статьи, о них мы поговорим в другой раз. Сейчас же, хочу еще раз напомнить, что от короткого замыкания не спасает УЗО, его предназначение совсем в другом.
Для того, чтобы правильно выбрать защитный автоматический выключатель, делаются расчеты величины возможного тока короткого замыкания для конкретной электроустановки. Чтобы в случае, если КЗ произойдёт, автоматика сработала оперативно, не пропустив резко возросший ток и не сгорев от него, не успев разорвав цепь.
Причины короткого замыкания
Чаще всего в бытовых условиях квартиры или частного дома, короткое замыкание возникает по нескольким причинам, основные из которых:
– в следствии нарушения изоляции электрических проводов или мест их соединений. Факторов приводящих к этому достаточно много, здесь и банальное старение материалов, и механическое повреждение, и даже загрязнения изоляторов.
– из-за случайного или преднамеренного соединения проводников с различным потенциалом, чаще всего фазного и нулевого. Это может быть вызвано ошибками при работе с электропроводкой под напряжением, неисправностью электроприборов, случайным попаданием проводников на контактные группы и т.д.
Поэтому, очень важно ответственно относится как к монтажу электроустановки, так и к её эксплуатации и обслуживанию.
Будьте аккуратны и осмотрительны при обращении с электрическими приборами и оборудованием, не включайте их в сеть если они повреждены или открыты. Не хватайтесь за электрические провода, если точно не знаете, что они не под напряжением.
Ну и как всегда, если у вас есть что добавить, вы нашли неточности или ошибки – обязательно пишите в комментариях к статье, кроме того задавайте свои вопросы, делитесь полезным опытом.
Что такое короткое замыкание? — CMP Products Limited
Тип продуктаКабельные скобы (12)Кабельные вводы (106)
Правила монтажа оборудованияAS/NZS, для горнодобывающей отрасли (Группа I) (15)Зоны AS/NZS (48)Разделы класса CEC (20)Зоны класса CEC (26)CEC, не классифицировано (3)GOST Zones (36)IEC, для горнодобывающей отрасли (Группа I) (14)IEC, не классифицировано (45)Зоны IEC (49)Разделы класса NEC (19)Зоны класса NEC (19)NEC, не классифицировано (3)Зоны Norsok (11)Параллельная конструкция (8)Один кабель (8)Трехлистная компоновка кабелей (7)
Тип защиты1Ex d IIC Gb X (27)1Ex e IIC Gb X (36)2Ex nR IIC Gc X (27)Класс I, Разд. 1 (8)Класс I, Разд. 1, Группы A, B, C, D (8)Класс I, Разд. 2 (18)Класс I, Разд. 2, Группы A, B, C, D (17)Класс I, Группы A, B, C, D (6)Класс I, Группы B, C, D (2)Класс I, Зона 1 (19)Класс I, Зона 1, AEx d IIC Gb (10)Класс I, Зона 1, AEx e IIC Gb (19)Класс I, Зона 2 (19)Класс I, Зона 2, AEx d IIC Gb (10)Класс I, Зона 2, AEx e IIC Gb (12)Класс I, Зона 2, AEx nR IIC Gc (8)Класс I, Зона 20 (10)Класс I, Зона 20, AEx ta IIIC Da (10)Класс I, Зона 21 (10)Класс I, Зона 21, AEx tb IIIC Db (10)Класс I, Зона 22 (10)Класс I, Зона 22, AEx tc IIIC Dc (10)Класс II, Разд. 1 (10)Класс I, Разд. 1, Группы E, F, G (10)Класс II, Разд. 2 (18)Класс II, Разд. 2, Группы E, F, G (18)Класс III, Разд. 1 (15)Класс III, Разд. 2 (13)Ex d I Mb (20)Ex d IIC Gb (36)Ex db I Mb (1)Ex db IIC Gb (1)Ex e I Mb (20)Ex e IIC Gb (46)Ex eb I Mb (1)Ex eb IIC Gb (3)Ex nR IIC Gc (34)Ex nRc IIC Gc (1)Ex ta IIIC Da (43)Ex ta IIIC Da X (35)Ex tb IIIC Db (43)Ex tb IIIC Db X (35)Ex tc IIIC Dc (43)Ex tc IIIC Dc X (35)Ex tD A21 IP66 (2)Промышленного назначения (45)Стандартные среды (6)Одноболтовой (10)Двухболтовой (10)Влажные среды (6)
Тип кабеляАлюминиевая ленточная броня (ASA) (25)Алюминиевая ленточная броня (например, ATA) (24)Алюминиевая проволочная броня (AWA) (34)Оснащенные броней и оболочкой (24)Судовой кабель с броней в виде оплетки (24)Гофрированная металлическая броня, приваренная непрерывным швом (MC-HL) — алюминий (4)Гофрофольгированная броня, приваренная непрерывным швом (MC-HL) — сталь (4)Гофрированная и взаимосвязанная металлическая броня (MC) — алюминий (4)Гофрированная и взаимосвязанная металлическая броня (MC) — сталь (4)Сверхтвердый шнур (2)Небронированный кабель плоской формы (2)Гибкий шнур (5)Освинцованный кабель с алюминиевой проволочной броней (LC/AWA) (9)Освинцованный кабель с гибкой проволочной броней (LC/PWA) (8)Освинцованный кабель с однослойной проволочной броней (LC/SWA) (9)Освинцованный кабель со стальной ленточной броней (LC/STA) (8)Освинцованный кабель с ленточной броней (LC/ASA) (8)Освинцованный кабель с броней в виде проволочной оплетки (8)Освинцованный небронированный кабель (2)M10 (12)M12 (8)Морской судовой кабель с броней в виде оплетки (24)Морской судовой кабель (11)Небронированный морской судовой кабель (19)Гибкая проволочная броня (PWA) (27)Оплетка и алюминиевая проволочная броня (AWA) (4)Оплетка и однослойная проволочная броня (SWA) (4)Гибкая проволочная (EMC) оплетка (например, CY/SY) (42)Однослойная проволочная броня (SWA) (38)Стальная ленточная броня (STA) (24)TECK (4)TECK 90 (4)TECK 90-HL (4)Кабель, укладывающийся в короб (9)Без брони (27)Броня в виде проволочной оплетки (42)
Конфигурация уплотненияДвойное наружное уплотнение (3)Внутреннее и наружное уплотнения (28)Внутреннее защитное уплотнение и кабельный ввод (2)Внутреннее защитное уплотнение и наружное уплотнение (18)Внутреннее защитное уплотнение и наружное уплотнение/переходная муфта FRAS (1)Без уплотнения (4)Наружное уплотнение (46)Наружное уплотнение/кабельный ввод (3)Наружное уплотнение/переходная муфта FRAS (1)Очень высокая (12)
СертификатыABS (67)Алюминий (3)Алюминий/нержавеющая сталь (1)ATEX (61)BS 6121 (45)BV (40)c-CSA-us (19)CCO-PESO (44)CSA (11)DNV-GL (41)Алюминий, покрытый эпоксидным составом (2)ГОСТ К (74)ГОСТ Р (44)IEC 62444 (45)IECEX (61)INMETRO (30)KCC (27)Lloyds (70)LSF (2)Одобренный LUL (Лондонский метрополитен) полимер (2)NEPSI (34)Нейлон (2)RETIE (35)Нержавеющая сталь (6)TR-CU-EAC (38)UL (9)
Защита от влагиОсевая нагрузка (12)Горизонтальная нагрузка (12)Нет (68)Силы при коротком замыкании (8)Да (41)
Замыкания в Javascript
В этом уроке мы с вами разберем, что такое замыкания. Замыкания, это именно тот вопрос, который чаще всего задают на собеседованиях. Обычно потому, что люди его не полностью понимают или не могут обьяснить.
Давайте начнем.
Итак представьте, что у нас есть функция, которая добавляет 2 к числу, которое мы указали в функции.
var addTo = function (passed) {
var inner = 2
return passed + inner
}
console.log(addTo(3))
То есть при вызове функции с аргументом 3 мы добавляем 3 + 2 и получаем 5.
Если мы посмотрим в браузер, то оно так и работает.
Теперь изменим код. Давайте уберем аргумент passed. Теперь вопрос: «Откуда мы можем его получить, чтобы он появился внутри функции addTo»?
Мы можем написать passed, как переменную вверху нашей функции и тогда она будет доступна внутри нашей функции.
var passed = 3
var addTo = function () {
var inner = 2
return passed + inner
}
console.log(addTo())
Если мы посмотрим в браузер, то мы получаем такой же результат, как до этого.
В javascript все переменные, который описаны снаружи функций доступны внутри этих функций. Именно поэтому мы можем брать переменную passed снаружи и использовать ее внутри нашей функции.
Теперь давайте усложним наш пример. Давайте внутри нашей функции addTo, которая принимает 1 аргумент passed, создадим еще одну функцию так, как мы их обычно создаем.
var addTo = function (passed) {
var add = function () {
}
}
И вернем созданную функцию не вызывая ее.
var addTo = function (passed) {
var add = function () {
}
return add
}
Теперь давайте добавим в эту функцию один параметр inner и так как мы помним из предыдущего пример, что внутри функции нам доступны внешние переменные, то нам доступна переменная passed в этой функции.
var addTo = function (passed) {
var add = function (inner) {
return passed + inner
}
return add
}
Итак еще раз. Мы описали функцию внутри функции, которая принимает аргумент inner и возвращает passed + inner. passed мы берем извне.
Теперь давайте напишем
var addThree = addTo(3)
console.log(addThree)
В браузере мы увидим, что у нас вывелась функция. И это логично, так как мы описали внутри функцию и ее вернули.
Как вы помните, функции являются обьектами, поэтому давайте посмотрим на свойства этого обьекта.
var addThree = addTo(3)
console.dir(addThree)
Теперь в консоли мы увидим не тело функции, а обьект. И у него есть одно свойство, которое нас интересует. Это свойство [[Scopes]]. Если мы его откроем, то увидим Closure внутри. И внутри Closure у нас и будет наша переменная passed, которую мы передавали как аргумент.
Собственно, Closure в переводе на русский и означает замыкание.
Итак еще раз. Как вы помните все переменные внутри функции создаются при ее вызове и удаляются, после завершения функции.
Но в данном случае мы видим, что функция addTo уже вызвалась, а переменная passed все еще хранится внутри и мы можем ее использовать.
Давайте попробуем вызвать эту функцию еще раз
var addThree = addTo(3)
var addOne = addTo(1)
console.dir(addThree)
console.dir(addOne)
Если мы посмотрим сейчас, то функция вызвалась в обоих случаях и в обоих случаях сохранила внутри себя passed после вызова.
Теперь мы можем этот passed использовать. Собственно он у нас и используется внутри нашей функции add. Но мы эту функцию еще не вызвали. Мы ее только вернули и записали в переменные.
Когда мы ее вызовем сейчас, то у нас будет доступ к переменной passed, так как она сохранилась в замыкании.
var addThree = addTo(3)
var addOne = addTo(1)
console.log(addThree(3))
console.log(addOne(2))
Если мы посмотрим в браузер, то у нас вывелось 6 и 3 соответственно.
Итак давайте подытожим, что такое замыкания.
- Это внутренная функция, со всеми внешними переменныеми, которые ей доступны.
- Замыкания служат для одной единственной цели — сохранить какие-то переменные, после вызова функции.
Это очень часто используемый прием в javascript, который мы будем применять в следующих уроках.
Если у вас возникли какие-то вопросы или комментарии, пишите их прямо под этим видео.
Длинное замыкание, что это? Основы электричества простыми словами. | ремонт своими руками
Приветствую на своём канале, в прошлой статье, мы разобрали, что такое фаза и ноль. Из неё мы поняли, что все электроприборы соединяют фазу и ноль вместе и поэтому работают.
Однако, тогда возникает вопрос, если мы на прямую возьмём фазовый провод и нулевой и соединим их вместе, будет что? Правильно, всё пыхнет, задымится, вышибет автоматы, пожар, катастрофа, спасаем детей, собак и кошек, любимая — бросай всё и спасайся сама… Что то я расфантазировался… Вернёмся к теме.
Случится короткое замыкание — это всем понятно, но почему? Ведь почти все электроприборы, подключённые к сети делают тоже самое… Соединяют фазу и ноль.
Очень интересное и не совсем понятное явление, будем разбираться.
Если мы соединяем провода друг с другом, получаем что? — Правильно. Короткое замыкание.
Если мы соединяем провода через «нагрузку», лампочку к примеру, получаем что? — Неправильно. Получим мы, не «ничего», а так называемое «Длинное замыкание».
То есть, ток будет проходить через лампочку, которая является сопротивлением и забирает свою часть преобразуя электрическую энергию, в тепловую.
Но, если мы соединяем провода без нагрузки (лампочки), сопротивления нет, сила тока многократно увеличивается, ток стремится как можно быстрее убежать по пути наименьшего сопротивления. Проводка, люди, кони и имущество, просто не могут выдержать такого потока мощной разрушительной энергии и всё горит и плавится.
КЗТок короткого замыкания, во много раз превышает токи штатной, «нормальной» работы электросети.
Почему ток резко возрастает, объясняет закон Ома.
Если коротко, он говорит о том, что ток равен напряжению разделённому на сопротивление (i=u/r).
Из этого следует, что если убрать из этой формулы сопротивление (именно это происходит при к. з. — мы делаем значение сопротивления очень маленьким), cила тока, резко возрастает.
Проводка не рассчитана на такое количество энергии, а деваться ей куда то нужно. Поэтому, при КЗ провода очень быстро нагреваются и плавятся, а их изоляция от нагрева загорается.
Представьте водопроводную трубу, идёт эта труба к небольшому фонтанчику, в трубе куда больший напор воды, чем тот, что бьёт из фонтанчика, напор и количество воды фонтанчика, регулируется сопротивлением — краном, который, пропускает лишь малую часть.
Всё хорошо, всё здорово.
Нужно исправлять ситуацию, берём большую кувалду и шарахаем по трубе сделав огромное технологическое отверстие!
Вся вода, устремится выплеснутся именно из этой бреши, а не из фонтанчика. Почему? Да потому что, там для неё путь наименьшего сопротивления. И конечно же поток будет куда сильнее.
Подведём итог: Подключая прибор в сеть, мы просто открываем кранчик фонтанчика, а соединяя разнопотенциальные провода — ударяем кувалдой по трубе.
В первом случае, это штатная работа, во втором, аварийная и нужно срочно отключать бушующую энергию от источника пока всё не затопило, а в случае с электрикой, пока всё не сгорело.
Для этих целей существуют автоматические выключатели, как только проходящий через них ток начинает превышать допустимые значения, они размыкают цепь.
О том, как правильно выбрать автоматические выключатели и осуществить их монтаж, я расскажу в своей следующей статье, подписывайтесь чтобы не пропустить. было интересно? — Оставляйте комментарии, ставьте лайки.
Короткое замыкание. Что это такое и какие они бывают
Короткое замыкание. Каждый слышал это словосочетание. Многие видели надпись «Не закорачивать!» Часто, когда ломается какой-нибудь электроприбор, говорят: «Коротнуло!» И несмотря на негативный оттенок этих слов, профессионалы знают, что короткое замыкание – не печальный приговор. Иногда с коротким замыканием (КЗ) бороться бессмысленно, а порой и принципиально невозможно. В этой статье будут даны ответы на самые важные вопросы: что такое короткое замыкание и какие виды КЗ встречаются в технике.
Начнем рассматривать эти вопросы под необычным углом – узнаем, в каких случаях короткие замыкания неизбежны и где они не играют роль повреждений. Возьмем за оба конца обыкновенный металлический провод. Соединим концы вместе. Провод замкнулся накоротко – произошло КЗ. Но так как в цепи отсутствуют источники электрической энергии и нагрузка, такое короткое замыкание никакого вреда не несет. В некоторых областях электротехники КЗ, которое мы рассмотрели, играет на руку, например, в электрических аппаратах и электрических машинах.
Взглянем на однофазное реле или пускатель, в конструкции которых есть магнитная система с подвижными частями — электромагнит, притягивающий якорь. Из-за постоянно меняющейся полярности тока, текущего в обмотках электромагнита, его магнитный поток периодически становится равен нулю, что вызывает дребезжание якоря, появляются вибрации и характерное, знакомое всем электрикам гудение. Чтобы избавиться от этого явления, на торец сердечника электромагнита или якоря прикрепляют короткозамкнутый виток – кольцо или прямоугольник из меди или алюминия. Из-за явления электромагнитной индукции в витке создается ток, создающий свой магнитный поток, компенсирующий пропадание основного магнитного потока, создаваемого электромагнитом, что приводит к уменьшению или исчезновению вибраций, разрушающих конструкцию.
Так же на руку играет короткое замыкание и в роторе асинхронного электродвигателя. Благодаря взаимодействию магнитного поля, создаваемого обмотками статора, с короткозамкнутым ротором, в роторе по уже упомянутому закону появляются свои токи, создающие свое поле, что приводит ротор во вращение. Конечно, важно грамотное проектирование электродвигателя или электрического аппарата, чтобы токи, протекающие в короткозамкнутых элементах, не приводили к перегреву и порче изоляции основных обмоток.
Подобным образом понятие «короткое замыкание» используется применительно к трансформаторам. Люди, так или иначе связанные с энергетикой, знают, что одна из важнейших характеристик трансформатора – это напряжение короткого замыкания, UКЗ, измеряемое в процентах. Возьмем трансформатор. Одну из его обмоток, скажем, низшего напряжения (НН) закоротим амперметром, сопротивление которого, как известно, принимается равным нулю. Обмотку высшего напряжения (ВН) подключаем к источнику напряжения. Повышаем напряжение на обмотке ВН до тех пор, пока ток в обмотке НН не станет равным номинальному, фиксируем это напряжение. Делим его на номинальное напряжение высшей стороны, умножаем на 100%, получаем UКЗ. Эта величина характеризует потери мощности в трансформаторе и его сопротивление, от которого зависит ток короткого замыкания, ведущий к повреждениям.
Поговорим наконец о коротких замыканиях, несущих негативные последствия. Такие короткие замыкания появляются, когда ток от источника питания протекает не через нагрузку, а только через провода, обладающие ничтожно маленьким сопротивлением. Например, трехфазный кабель питается от трансформатора, и одним неосторожным движением ковша экскаватора происходит его повреждение – две фазы закорачиваются через ковш. Такое КЗ называют двухфазным. Аналогично по количеству замкнутых фаз называют другие КЗ. Однофазное замыкание на землю в сетях с изолированной нейтралью не является коротким, но может представлять угрозу жизни живых существ. Металлическим называют КЗ, в котором переходное сопротивление равно нулю – например, при болтовом или сварочном соединении. Токи КЗ в зависимости от напряжения и вида повреждения могут достигать тысяч и сотен тысяч ампер, приводить к пожарам и колоссальным электродинамическим усилиям, «выворачивающим» шины и провода. Защита от КЗ может осуществляться автоматическими выключателями или предохранителями, а в высоковольтных сетях – средствами релейной защиты и автоматики.
Главное же средство от повреждающих коротких замыканий – грамотное проектирование, эксплуатация, и своевременная проверка средств защиты от КЗ.
замыканий — JavaScript | MDN
Замыкание — это комбинация функции, объединенной (заключенной) со ссылками на ее окружающее состояние (лексическая среда ). Другими словами, замыкание дает вам доступ к области внешней функции из внутренней функции. В JavaScript замыкания создаются каждый раз, когда создается функция, во время создания функции.
Рассмотрим следующий пример кода:
function init () {
var name = 'Mozilla';
function displayName () {
оповещение (имя);
}
показать имя();
}
в этом();
init ()
создает локальную переменную с именем name
и функцию с именем displayName ()
.Функция displayName ()
— это внутренняя функция, которая определена внутри init ()
и доступна только в теле функции init ()
. Обратите внимание, что функция displayName ()
не имеет собственных локальных переменных. Однако, поскольку внутренние функции имеют доступ к переменным внешних функций,
displayName ()
может получить доступ к переменной name
, объявленной в родительской функции, init ()
.
Запустите код, используя эту ссылку JSFiddle, и обратите внимание, что оператор alert ()
в функции displayName ()
успешно отображает значение переменной name
, которая объявлена в ее родительской функции.Это пример лексического области видимости , который описывает, как синтаксический анализатор разрешает имена переменных, когда функции вложены. Слово лексический относится к тому факту, что лексическая область видимости использует место, где объявлена переменная в исходном коде, чтобы определить, где эта переменная доступна. Вложенные функции имеют доступ к переменным, объявленным во внешней области видимости.
Рассмотрим следующий пример кода:
function makeFunc () {
var name = 'Mozilla';
function displayName () {
оповещение (имя);
}
return displayName;
}
var myFunc = makeFunc ();
myFunc ();
Выполнение этого кода имеет тот же эффект, что и предыдущий пример функции init ()
выше. Что отличается (и интересно), так это то, что внутренняя функция
displayName ()
возвращается из внешней функции перед выполнением .
На первый взгляд может показаться нелогичным, что этот код все еще работает. В некоторых языках программирования локальные переменные внутри функции существуют только на время выполнения этой функции. После завершения выполнения makeFunc ()
можно ожидать, что переменная name больше не будет доступна. Однако, поскольку код по-прежнему работает должным образом, в JavaScript это явно не так.
Причина в том, что функции в JavaScript закрывают формы. Замыкание — это комбинация функции и лексической среды, в которой эта функция была объявлена. Эта среда состоит из любых локальных переменных, которые были в области видимости во время создания замыкания. В этом случае myFunc
— это ссылка на экземпляр функции displayName
, которая создается при запуске makeFunc
. Экземпляр
displayName
поддерживает ссылку на свою лексическую среду, в которой существует переменная name
.По этой причине, когда вызывается myFunc
, переменная name
остается доступной для использования, а «Mozilla» передается в alert
.
Вот немного более интересный пример — функция makeAdder
:
function makeAdder (x) {
return function (y) {
вернуть x + y;
};
}
var add5 = makeAdder (5);
var add10 = makeAdder (10);
console.log (add5 (2));
console.log (add10 (2));
В этом примере мы определили функцию makeAdder (x)
, которая принимает единственный аргумент x
и возвращает новую функцию.Возвращаемая функция принимает единственный аргумент y
и возвращает сумму x
и y
.
По сути, makeAdder
— это фабрика функций. Он создает функции, которые могут добавлять определенное значение к своему аргументу. В приведенном выше примере фабрика функций создает две новые функции: одна добавляет пять к своему аргументу, а вторая добавляет 10.
add5
и add10
— оба закрытия. Они используют одно и то же определение тела функции, но хранят разные лексические среды.В лексической среде add5
x
равно 5, а в лексической среде для add10
x
равно 10.
Замыкания полезны, потому что они позволяют связывать данные (лексическое окружение) с функцией. который работает с этими данными. В этом есть очевидные параллели с объектно-ориентированным программированием, где объекты позволяют связывать данные (свойства объекта) с одним или несколькими методами.
Следовательно, вы можете использовать замыкание везде, где вы обычно можете использовать объект только с одним методом.
Ситуации, в которых вы, возможно, захотите это сделать, особенно распространены в Интернете. Большая часть кода, написанного на интерфейсном JavaScript, основана на событиях. Вы определяете какое-то поведение, а затем присоединяете его к событию, запускаемому пользователем (например, щелчком или нажатием клавиши). Код прикреплен как обратный вызов (единственная функция, которая выполняется в ответ на событие).
Например, предположим, что мы хотим добавить на страницу кнопки для регулировки размера текста. Один из способов сделать это — указать размер шрифта элемента body (в пикселях), а затем установить размер других элементов на странице (например, заголовков), используя относительную единицу
em
:
кузов {
семейство шрифтов: Helvetica, Arial, sans-serif;
размер шрифта: 12 пикселей;
}
h2 {
размер шрифта: 1.5em;
}
h3 {
размер шрифта: 1.2em;
}
Такие интерактивные кнопки размера текста могут изменять свойство font-size
элемента body
, и настройки принимаются другими элементами на странице благодаря относительным единицам.
Вот код JavaScript:
function makeSizer (size) {
return function () {
document.
body.style.fontSize = size + 'px';
};
}
var size12 = makeSizer (12);
var size14 = makeSizer (14);
var size16 = makeSizer (16);
size12
, size14
и size16
теперь являются функциями, которые изменяют размер основного текста на 12, 14 и 16 пикселей соответственно.Вы можете прикрепить их к кнопкам (в данном случае гиперссылкам), как показано в следующем примере кода.
document.getElementById ('размер-12'). Onclick = size12;
document.getElementById ('размер-14'). onclick = size14;
document.getElementById ('размер-16'). onclick = size16;
12
14
16
Запустите код с помощью JSFiddle.
Такие языки, как Java, позволяют объявлять методы как частные, что означает, что они могут вызываться только другими методами того же класса.
JavaScript не предоставляет собственный способ сделать это, но можно имитировать частные методы с помощью замыканий. Частные методы полезны не только для ограничения доступа к коду. Они также предоставляют мощный способ управления вашим глобальным пространством имен.
В следующем коде показано, как использовать замыкания для определения общедоступных функций, которые могут обращаться к частным функциям и переменным. Обратите внимание, что эти закрытия соответствуют шаблону проектирования модуля.
var counter = (function () {
var privateCounter = 0;
function changeBy (val) {
privateCounter + = val;
}
возвращаться {
инкремент: функция () {
changeBy (1);
},
декремент: function () {
changeBy (-1);
},
value: function () {
return privateCounter;
}
};
}) ();
консоль.журнал (counter.value ());
counter.increment ();
counter.increment ();
console.log (counter.value ());
counter.decrement ();
console.log (counter.value ());
В предыдущих примерах каждое замыкание имело свое собственное лексическое окружение. Однако здесь существует единая лексическая среда, которая используется тремя функциями:
counter.increment
, counter.decrement
и counter.value
.
Совместно используемая лексическая среда создается в теле анонимной функции , которая выполняется, как только она была определена (также известной как IIFE).Лексическая среда содержит два закрытых элемента: переменную с именем privateCounter
и функцию с именем changeBy
. Вы не можете получить доступ ни к одному из этих закрытых членов извне анонимной функции. Вместо этого вы можете получить к ним доступ с помощью трех общедоступных функций, возвращаемых анонимной оболочкой.
Эти три публичные функции представляют собой замыкания, которые используют одну и ту же лексическую среду. Благодаря лексической области видимости JavaScript каждый из них имеет доступ к переменной privateCounter
и функции changeBy
.
var makeCounter = function () {
var privateCounter = 0;
function changeBy (val) {
privateCounter + = val;
}
возвращаться {
инкремент: функция () {
changeBy (1);
},
декремент: function () {
changeBy (-1);
},
value: function () {
return privateCounter;
}
}
};
var counter1 = makeCounter ();
var counter2 = makeCounter ();
предупреждение (counter1.
value ());
counter1.increment ();
counter1.increment ();
предупреждение (counter1.value ());
counter1.decrement ();
предупреждение (counter1.ценить());
предупреждение (counter2.value ());
Обратите внимание, как два счетчика сохраняют независимость друг от друга. Каждое замыкание ссылается на другую версию переменной privateCounter
через собственное замыкание. Каждый раз, когда вызывается один из счетчиков, его лексическое окружение изменяется, изменяя значение этой переменной. Изменения значения переменной в одном закрытии не влияют на значение в другом закрытии.
Использование замыканий таким образом дает преимущества, обычно связанные с объектно-ориентированным программированием.В частности, данные скрывают инкапсуляцию и .
Каждое укупорочное средство имеет три области применения:
- Локальная область действия (Собственная область действия)
- Объем внешних функций
- Глобальный охват
Распространенной ошибкой является непонимание того, что в случае, когда внешняя функция сама является вложенной функцией, доступ к области действия внешней функции включает в себя область действия внешней функции, что фактически создает цепочку областей действия функций. Для демонстрации рассмотрим следующий пример кода.
var e = 10;
function sum (a) {
return function (b) {
return function (c) {
return function (d) {
вернуть a + b + c + d + e;
}
}
}
}
console.log (сумма (1) (2) (3) (4));
var e = 10;
function sum (a) {
return function sum2 (b) {
функция возврата sum3 (c) {
функция возврата sum4 (d) {
вернуть a + b + c + d + e;
}
}
}
}
var s = sum (1);
var s1 = s (2);
var s2 = s1 (3);
var s3 = s2 (4);
console.log (s3)
В приведенном выше примере есть серия вложенных функций, каждая из которых имеет доступ к области внешних функций.В этом контексте мы можем сказать, что замыкания имеют доступ к всем областям внешних функций .
До введения ключевого слова let
в ECMAScript 2015 общая проблема с замыканиями возникала, когда вы создавали их внутри цикла. Для демонстрации рассмотрим следующий пример кода.
Здесь появятся полезные примечания
Электронная почта:
Имя:
Возраст:
function showHelp (help) {
документ.getElementById ('помощь'). textContent = help;
}
function setupHelp () {
var helpText = [
{'id': 'email', 'help': 'Ваш адрес электронной почты'},
{'id': 'name', 'help': 'Ваше полное имя'},
{'id': 'age', 'help': 'Ваш возраст (вы должны быть старше 16 лет)'}
];
for (var i = 0; i
Попробуйте запустить код в JSFiddle.
Массив helpText
определяет три полезных подсказки, каждая из которых связана с идентификатором поля ввода в документе. Цикл циклически перебирает эти определения, связывая событие onfocus
с каждым, которое показывает связанный метод справки.
Если вы попробуете этот код, вы увидите, что он работает не так, как ожидалось. Независимо от того, на какой области вы сосредоточитесь, будет отображаться сообщение о вашем возрасте.
Причина этого в том, что функции, назначенные для onfocus
, являются закрытием; они состоят из определения функции и захваченного окружения из области действия функции setupHelp
.Цикл создал три замыкания, но каждое из них использует одну и ту же лексическую среду, в которой есть переменная с изменяющимися значениями (, элемент
). Это связано с тем, что переменная item
объявлена с var
и, таким образом, имеет область действия из-за подъема. Значение item.help
определяется, когда выполняются обратные вызовы onfocus
. Поскольку к этому времени цикл уже завершился, объект переменной item
(общий для всех трех замыканий) остался указывающим на последнюю запись в списке helpText
.
Одним из решений в этом случае является использование большего количества замыканий: в частности, использование фабрики функций, как описано ранее:
function showHelp (help) {
document.getElementById ('справка'). textContent = help;
}
function makeHelpCallback (help) {
return function () {
showHelp (помощь);
};
}
function setupHelp () {
var helpText = [
{'id': 'email', 'help': 'Ваш адрес электронной почты'},
{'id': 'name', 'help': 'Ваше полное имя'},
{'id': 'age', 'help': 'Ваш возраст (вы должны быть старше 16 лет)'}
];
for (var i = 0; i
Запустите код, используя эту ссылку JSFiddle.
Работает, как ожидалось. Вместо того, чтобы все обратные вызовы совместно использовали единую лексическую среду, функция makeHelpCallback
создает новую лексическую среду для каждого обратного вызова, в которой help
ссылается на соответствующую строку из массива helpText
.
Еще один способ записать вышеизложенное с использованием анонимных замыканий:
function showHelp (help) {
document.getElementById ('справка'). textContent = help;
}
function setupHelp () {
var helpText = [
{'id': 'email', 'help': 'Ваш адрес электронной почты'},
{'id': 'name', 'help': 'Ваше полное имя'},
{'id': 'age', 'help': 'Ваш возраст (вы должны быть старше 16 лет)'}
];
for (var i = 0; i
Если вы не хотите использовать больше замыканий, вы можете использовать ключевое слово let
, представленное в ES2015:
function showHelp (help) {
document.getElementById ('справка'). textContent = help;
}
function setupHelp () {
var helpText = [
{'id': 'email', 'help': 'Ваш адрес электронной почты'},
{'id': 'name', 'help': 'Ваше полное имя'},
{'id': 'age', 'help': 'Ваш возраст (вы должны быть старше 16 лет)'}
];
for (пусть i = 0; i длина; i ++) {
let item = helpText [i];
document.getElementById (item.id) .onfocus = function () {
showHelp (item.help);
}
}
}
setupHelp ();
В этом примере используется let
вместо var
, поэтому каждое замыкание связывает переменную с блочной областью видимости, что означает, что никаких дополнительных замыканий не требуется.
Другой альтернативой может быть использование forEach ()
для перебора массива helpText
и присоединения слушателя к каждому
, как показано:
function showHelp (help) {
документ.getElementById ('помощь'). textContent = help;
}
function setupHelp () {
var helpText = [
{'id': 'email', 'help': 'Ваш адрес электронной почты'},
{'id': 'name', 'help': 'Ваше полное имя'},
{'id': 'age', 'help': 'Ваш возраст (вы должны быть старше 16 лет)'}
];
helpText.forEach (function (text) {
document.getElementById (text.
id) .onfocus = function () {
showHelp (text.help);
}
});
}
setupHelp ();
Неразумно без надобности создавать функции в других функциях, если замыкания не нужны для конкретной задачи, так как это отрицательно скажется на производительности скрипта как с точки зрения скорости обработки, так и с точки зрения потребления памяти.
Например, при создании нового объекта / класса методы обычно должны быть связаны с прототипом объекта, а не определены в конструкторе объекта. Причина в том, что всякий раз, когда вызывается конструктор, методы будут переназначены (то есть для каждого создания объекта).
Рассмотрим следующий случай:
function MyObject (имя, сообщение) {
this.name = name.toString ();
this.message = message.toString ();
this.getName = function () {
верни это.имя;
};
this.getMessage = function () {
вернуть this.message;
};
}
Поскольку предыдущий код не использует преимущества использования замыканий в этом конкретном экземпляре, мы могли бы вместо этого переписать его, чтобы избежать использования замыканий, следующим образом:
function MyObject (имя, сообщение) {
this.
name = name.toString ();
this.message = message.toString ();
}
MyObject.prototype = {
getName: function () {
вернуть this.name;
},
getMessage: function () {
верни это.сообщение;
}
};
Однако переопределять прототип не рекомендуется. Следующий пример вместо этого добавляется к существующему прототипу:
function MyObject (имя, сообщение) {
this.name = name.toString ();
this.message = message.toString ();
}
MyObject.prototype.getName = function () {
вернуть this.name;
};
MyObject.prototype.getMessage = function () {
вернуть this.message;
};
В двух предыдущих примерах унаследованный прототип может использоваться всеми объектами, и определения методов не обязательно должны появляться при создании каждого объекта.См. Подробности об объектной модели для получения дополнительной информации.
c ++ - Каждая функция закрывается?
Замыкания - это эффективный способ реализации функций .
Я утверждаю, что каждая функция концептуально закрытие, даже на нескольких языках, в которых их нет.
Закрытые переменные являются константами или статическими данными внутри кода. Но в полноценных замыканиях (например, в Ocaml, Scheme, Common Lisp или C ++ 11) закрытые переменные и код находятся в самом закрытии.
Например, в C (и в C ++ 98) функции являются замыканиями, но их закрытые (или свободные ) переменные ограничены статическими
или глобальными переменными.
Closures также дает возможность для анонимных функций, например лямбда-выражения, которые создают замыкания с новыми закрытыми переменными (а это невозможно в стандартном C). Это абстракция , операция λ-исчисления (которой нет в C, поэтому каждая функция обратного вызова -e.грамм. в GTK- принимает некоторые "данные клиента" как дополнительный параметр).
С точки зрения реализации, функция всегда представляет собой некоторый код с некоторыми данными, необходимыми для этого кода. Данные - это закрытые переменные. Для скомпилированного кода C данные связаны в коде как фиксированные глобальные или статические переменные (и изменение этих переменных требует перекомпиляции) или как буквальные константы. Таким образом, единственный способ сделать абстракцию - это регенерировать некоторый новый код с некоторыми новыми переменными в нем.
Абстракция не существует в стандарте C, потому что нет способа генерировать переносимые новые функции. Но вы можете использовать странные уловки, зависящие от конкретной реализации. Представьте, что вы хотите реализовать функцию генератора переводов. В ocaml функция для генерации переведенной на дельта
функции -
позвольте перевести дельту = веселье x -> x + delta
, и это создает новое закрытие для каждого вызова. Так в
пусть t3 = transl 3 в t3 5
создается новое закрытие t3
(но, возможно, оптимизатор удаляет его), и результат равен 8 (что составляет 3 + 5).
Вы можете проделывать безумные трюки в C: в Linux, например, сгенерирует во время выполнения новый текстовый файл t3mod.c
, содержащий
int t3 (int y) {вернуть y + 3; };
, и вы скомпилируете этот файл t3mod.c
в общий объект t3mod.so
и dlopen
, а затем dlsym
, используя "t3"
. Этот надуманный способ (использование генерации кода C во время выполнения с последующей его динамической загрузкой) представляет собой способ реализации абстракций в C и способ динамически генерировать новых указателей на функции.Точно так же вы можете использовать библиотеки компиляции JIT, например, libjit, LLVM (или libgccjit
в будущем GCC 5). Прочтите также о частичной оценке, eval и многоэтапном программировании (например, Meta OCaml ...). См. Также записи в блоге Дж. Питрата о мета-комбинаторном поиске и мета-ошибках.
- Являются ли методы класса типом закрытия?
Являются ли методы класса типом закрытия?
Нет. Закрытие - это очень специфический тип функции.Как говорит Википедия, они реализуют лексическую область видимости.
Укупорочные средства
В Python такое закрытие выглядело бы так:
по умолчанию startAt (x):
def incrementBy (y):
вернуть x + y
return incrementBy
Переменная x
связана (в случае Python - неизменяемо) в incrementBy
, который создается при вызове startAt
. ( x
вполне может быть изменяемой структурой данных , либо реализация вашего языка может позволить вам изменить ее.)
Для сравнения, вот пример функции без закрытия, в которой используется глобальный:
х = 0
def incrementBy (y):
вернуть x + y
Здесь есть одна функция, incrementBy
, и всякий раз, когда x изменяется в глобальной области видимости, это отражается в функции:
>>> incrementBy (5)
5
>>> х = 1
>>> incrementBy (5)
6
Методы
С другой стороны, методы имеют доступ к изменяемым переменным. Если это методы экземпляра, они знают свой экземпляр и, следовательно, класс. Если это методы класса, они знают свой класс. Даже если это статические методы, не знающие класса или экземпляра, они все равно могут использовать глобальные переменные. Глобальные объекты могут быть переназначены по желанию и, таким образом, могут изменять выходные данные функции для любого заданного значения.
Вы, , можете сделать закрытие метода, добавленного к классу, в зависимости от реализации вашего языка:
класс Foo (объект):
ib5 = статический метод (startAt (5))
>>> Фу.ib5 (3)
8
>>> Foo.ib5
<приращение функции по адресу 0x7f29e978b758>
Но я не думаю, что вы на самом деле хотите это сделать.
Что такое закрытие?
Замыкание - это внутренняя функция, которая связывает с ней переменные из области внешней функции. Если метод класса этого не делает, это не закрытие.
Сравнение и противопоставление методов закрытия
Их объединяет функциональность и данные.
Там, где они контрастируют, замыкания связывают данные с функциональностью, тогда как методы связывают функциональность со структурами данных. Таким образом, было бы неправильно называть метод типом замыкания, если он действительно не является замыканием.
Я бы сказал, что большинство методов класса этого не делают, но допущу, что могут быть среды, в которых это признанный метод. Мне было бы очень интересно узнать, кто этим пользуется и каково их оправдание. Я лично никогда не видел, чтобы это делалось в дикой природе.
Что такое закрытие? - Calhoun.io
В предыдущей статье о тестировании с Go есть пример, в котором замыкание используется для создания тестов, управляемых таблицами.Это довольно распространенная практика в Go, но после написания статьи разработчик связался со мной и сделал следующий комментарий.
«Я кое-что узнал. В основном то, что я понятия не имею, как работают замыкания в Go »
Ой! Это означает, что, несмотря на то, что остальная часть статьи наполнена отличным контентом о тестировании, если читатель не понимает закрытия, он не сможет получить от статьи все преимущества.
Этот пост предназначен для решения этой проблемы.В нем мы собираемся обсудить, что такое замыкания и почему вы должны заботиться о них. Затем в следующей статье «5 полезных способов использования замыканий в Go» (избыточных, я знаю) мы рассмотрим некоторые из наиболее распространенных вариантов использования замыканий, чтобы помочь вам понять, где и когда использовать замыкания в вашем собственном коде. У каждого варианта использования есть конкретный пример, с которым я столкнулся, так что это не просто воображаемое использование.
Анонимные функции
Прежде чем мы перейдем к замыканиям, нам нужно сначала поговорить об анонимных функциях.Анонимная функция - это то же самое, что и обычная старая функция, но у нее нет имени - отсюда и термин «анонимный». Вместо этого анонимная функция создается динамически, как и переменная.
Как и большинство вещей, это проще объяснить с помощью кода. Ниже показано, как будет выглядеть обычная функция.
func DoStuff () {
// Делаем что-нибудь
}
Если бы мы хотели превратить эту же функцию в анонимную, мы бы не объявляли ее таким же образом; вместо того, чтобы начинать с ключевого слова func
, мы могли бы вместо этого создать переменную с типом func ()
. После этого мы могли создать и назначить анонимную функцию переменной.
var DoStuff func () = func () {
// Делаем что-нибудь
}
Между этими двумя есть много тонких различий, но для большинства практических целей большая разница заключается в том, что мы могли назначить новую функцию переменной DoStuff
во время выполнения, что позволяет нам динамически изменять то, что делает DoStuff ()
. .
пакет основной
импорт "FMT"
var DoStuff func () = func () {
// Делаем что-нибудь
}
func main () {
DoStuff ()
DoStuff = func () {
fmt.Println ("Делаю что-нибудь!")
}
DoStuff ()
DoStuff = func () {
fmt.Println ("Занимаюсь другими делами.")
}
DoStuff ()
}
Если вы запустите эту программу, вы увидите следующий результат.
Делаем всякие! Занимаюсь другими делами.
Мы видим две разные строки вывода, потому что мы объявили три разные анонимные функции, и каждая из последних двух выводит что-то свое.
Анонимные функции могут принимать параметры, возвращать данные и делать почти все, что может делать обычная функция.Вы даже можете присвоить переменной обычную функцию, как вы это делаете с анонимной функцией.
пакет основной
импорт "FMT"
var DoStuff func () = func () {
// Делаем что-нибудь
}
func RegFunc () {fmt.Println ("reg func")}
func main () {
DoStuff ()
DoStuff = RegFunc
DoStuff ()
}
Единственная реальная разница между обычной функцией и анонимной состоит в том, что анонимные функции не объявляются на уровне пакета. Они объявляются более динамично и обычно либо используются, а затем забываются, либо присваиваются переменной для последующего использования.
затворы
Замыкание - это особый тип анонимной функции, которая ссылается на переменные, объявленные вне самой функции. Это означает, что, как и в предыдущих примерах, мы будем создавать функцию динамически, но в этом случае мы будем использовать переменные, которые не были переданы в функцию в качестве параметра, но вместо этого были доступны при объявлении функции.
Это очень похоже на то, как обычная функция может ссылаться на глобальные переменные. Эти переменные не передаются в функцию напрямую в качестве параметра, но функция имеет к ним доступ при вызове.
Давайте посмотрим на закрытие в действии. В следующем примере мы собираемся создать замыкание, которое отслеживает, сколько раз оно было вызвано, и возвращает это число.
пакет основной
импорт "FMT"
func main () {
п: = 0
counter: = func () int {
п + = 1
вернуть n
}
fmt.Println (counter ())
fmt.Println (counter ())
}
Если вы запустите этот код, вы получите результат:
Обратите внимание, что наша анонимная функция имеет доступ к переменной n
, но она никогда не передавалась в качестве параметра при вызове counter ()
.Это то, что делает его закрытием!
Замыкания обеспечивают изоляцию данных
Одна проблема с предыдущим примером - проблема, которая также может всплывать при использовании глобальных переменных. Любой код внутри функции
main ()
имеет доступ к n
, поэтому можно увеличивать счетчик без фактического вызова counter ()
. Мы не этого хотим; Вместо этого мы предпочли бы изолировать n
, чтобы никакой другой код не имел к нему доступа.
Для этого нам нужно взглянуть на другой интересный аспект замыканий, который заключается в том, что они по-прежнему могут ссылаться на переменные, к которым у них был доступ во время создания, даже если на эти переменные больше нигде нет ссылок.
пакет основной
импорт "FMT"
func main () {
counter: = newCounter ()
fmt.Println (counter ())
fmt.Println (counter ())
}
func newCounter () func () int {
п: = 0
return func () int {
п + = 1
вернуть n
}
}
В этом примере наше замыкание ссылается на переменную n
даже после того, как функция newCounter ()
завершила работу. Это означает, что у нашего замыкания есть доступ к переменной, которая отслеживает, сколько раз оно было вызвано, но никакой другой код, кроме функции newCounter ()
, не имеет доступа к этой переменной. Это одно из многих преимуществ замыкания - мы можем сохранять данные между вызовами функций, а также изолировать данные от другого кода.
Хотите улучшить свои навыки игры в го?
Вы хотите попрактиковаться в го, но не можете придумать хороший проект для работы? Или, может быть, вы просто не знаете, какие техники и навыки вам нужно изучить дальше, поэтому не знаете, с чего начать. Если так, не волнуйтесь - я вас прикрыл!
Gophercises - это БЕСПЛАТНЫЙ курс, в котором мы работаем над задачами упражнений, каждая из которых предназначена для того, чтобы научить вас различным аспектам го.Сюда входят темы, начиная от базовых операций со строками и заканчивая более сложными темами, такими как функциональные параметры и параллелизм. В каждом упражнении есть образец решения, а также скринкаст (видео), где я кодирую решение, пока вы по коду показываете. К тому же суслики действительно милые 😉
БЕСПЛАТНЫЙ курс Суслики - упражнения для начинающих сусликовВперед…
В моем следующем посте - - я расскажу о нескольких типичных случаях использования замыканий, в том числе о том, как замыкания могут облегчить чтение и понимание кода, как замыкания могут предотвратить ад обратных вызовов, который так часто поднимается в javascript, и даже как может использовать замыкания для создания промежуточного программного обеспечения, которое увеличивает время выполнения ваших веб-приложений. Вам следует проверить это, чтобы вы могли легко распознать ситуации, в которых закрытие может быть полезным!
После этого у меня будет небольшая статья, посвященная подводным камням и типичным ошибкам, которые делают разработчики при создании замыканий, и способам их избежать.
Изучите веб-разработку с Go!
Подпишитесь на мою рассылку, и я пришлю вам БЕСПЛАТНЫЙ образец из моего курса - Веб-разработка с Go.Пример включает в себя несколько первых глав из книги и более 2,5 часов скринкастов.
Вы также будете получать от меня электронные письма о предстоящих курсах (включая БЕСПЛАТНЫЕ), новых сообщениях в блогах и скидках на курсы.
© 2018 Джонатан Калхун. Все права защищены.
Определение закрытия по Merriam-Webster
закрыто | \ ˈKlō-zhər \1 : акт закрытия : условие закрытия закрытие век закрытие бизнеса закрытие фабрики
2 : часто успокаивающее или удовлетворяющее чувство завершенности жертвы, нуждающиеся в закрытии также : что-то (например, удовлетворительный финал), дающее такое ощущение3 : то, что закрывает карман на молнии с защитой от детей
4 [перевод французского clôture ] : закрытие
5
: свойство, которое имеет система счисления или множество, когда они математически закрываются при выполнении операции.
6 : набор, который состоит из данного набора вместе со всеми предельными точками этого набора.
Простое объяснение закрытий в JavaScript
Обратные вызовы, обработчики событий, функции высшего порядка могут обращаться к переменным внешней области видимости благодаря замыканиям. Замыкания важны в функциональном программировании, и их часто спрашивают во время собеседования по кодированию JavaScript.
Затворы, которые используются повсеместно, трудно понять. Если вы еще не пробовали «Ага!» момент в понимании закрытия, тогда этот пост для вас.
Я начну с основных терминов: объем и лексический объем. Затем, усвоив основы, вам понадобится всего один шаг, чтобы окончательно понять замыкания.
Перед тем, как начать, я предлагаю вам не поддаваться желанию пропустить разделы области и лексической области.Эти концепции имеют решающее значение для закрытия, и если вы хорошо их усвоите, идея закрытия станет самоочевидной.
1. Область применения
Когда вы определяете переменную, вы хотите, чтобы она была доступна в определенных границах. Например. Переменная result
имеет смысл существовать в функции calculate ()
в качестве внутренней детали. Вне calculate ()
переменная result
бесполезна.
Доступность переменных управляется областью .Вы можете получить доступ к переменной, определенной в ее области видимости. Но вне этой области переменная недоступна.
В JavaScript область создается функцией или блоком кода.
Давайте посмотрим, как область действия влияет на доступность переменной count
. Эта переменная принадлежит области, созданной функцией foo ()
:
function foo () {
пусть count = 0;
console.log (количество); }
foo ();
console.log (количество);
count
свободно доступно в рамках foo ()
.
Однако за пределами области foo ()
счетчик
недоступен. Если вы все равно попытаетесь получить доступ к count
извне, JavaScript выдает ReferenceError: count is not defined
.
В JavaScript область видимости говорит: если вы определили переменную внутри функции или блока кода, то вы можете использовать эту переменную только в этой функции или блоке кода. Приведенный выше пример демонстрирует такое поведение.
Теперь давайте посмотрим на общую формулировку:
Область действия - это политика пространства, которая управляет доступностью переменных.
Непосредственно возникает свойство: область видимости изолирует переменных. Это замечательно, потому что различных областей видимости могут иметь переменные с одинаковыми именами .
Вы можете повторно использовать имена общих переменных ( счетчик
, индекс
, текущий
, значение
и т. Д.) В разных областях без конфликтов.
foo ()
и bar () области функций
имеют свои собственные, но с тем же именем переменные count
:
function foo () {
пусть count = 0;
консоль.журнал (количество); }
function bar () {
пусть count = 1;
console.log (количество); }
foo ();
бар();
count
переменных из областей видимости функций foo ()
и bar ()
не конфликтуют.
2. Вложенность областей применения
Давайте еще немного поиграем с прицелами и вставим один прицел в другой.
Функция innerFunc ()
вложена во внешнюю функцию outerFunc ()
.
Как 2 области действия функций будут взаимодействовать друг с другом? Могу ли я получить доступ к переменной outerVar
из outerFunc ()
из области innerFunc ()
?
Давайте попробуем это на примере:
function outerFunc () {
let outerVar = 'Я снаружи!';
function innerFunc () {
консоль.журнал (externalVar); }
innerFunc ();
}
externalFunc ();
Действительно, переменная outerVar
доступна внутри области innerFunc ()
. Переменные внешней области видимости доступны внутри внутренней области.
Теперь вы знаете 2 интересные вещи:
- Области могут быть вложены
- Переменные внешней области видимости доступны внутри внутренней области
3.

Как JavaScript понимает, что externalVar
внутри innerFunc ()
соответствует переменной outerVar
из outerFunc ()
?
Это потому, что JavaScript реализует механизм области видимости под названием лексическая область видимости (или статическая область видимости).Лексическая область видимости означает, что доступность переменных определяется положением переменных в исходном коде внутри областей вложенности.
Проще говоря, лексическая область видимости означает, что внутри внутренней области видимости вы можете получить доступ к переменным ее внешней области видимости.
Он называется лексическим (или статическим ), потому что движок определяет (во время лексирования) вложенность областей видимости, просто просматривая исходный код JavaScript, не выполняя его.
Вот как движок понимает предыдущий фрагмент кода:
- Я вижу, вы определяете функцию
outerFunc ()
с переменнойouterVar
.Хороший.
- Внутри
externalFunc ()
я вижу, что вы определяете функциюinnerFunc ()
. - Внутри
innerFunc ()
я вижу переменнуюouterVar
без объявления. Поскольку я использую лексическую область видимости, я считаю, что переменнаяouterVar
внутриinnerFunc ()
является той же переменной, что иouterVar
изouterFunc ()
.
Дистиллированное представление о лексической области:
Лексическая область состоит из внешних областей, определяемых статически.
Например:
const myGlobal = 0;
function func () {
const myVar = 1;
console.log (myGlobal);
function innerOfFunc () {
const myInnerVar = 2;
console.log (myVar, myGlobal);
function innerOfInnerOfFunc () {
console.log (myInnerVar, myVar, myGlobal); }
innerOfInnerOfFunc ();
}
innerOfFunc ();
}
func ();
Лексическая область видимости innerOfInnerOfFunc ()
состоит из областей видимости innerOfFunc ()
, func ()
и глобальной области (самой внешней области). Внутри
innerOfInnerOfFunc ()
вы можете получить доступ к переменным лексической области видимости myInnerVar
, myVar
и myGlobal
.
Лексическая область видимости innerFunc ()
состоит из func ()
и глобальной области видимости. Внутри innerOfFunc ()
вы можете получить доступ к переменным лексической области видимости myVar
и myGlobal
.
Наконец, лексическая область видимости func ()
состоит только из глобальной области видимости.В func ()
вы можете получить доступ к переменной лексической области видимости myGlobal
.
4. Укупорка
Хорошо, лексическая область видимости позволяет получить статический доступ к переменным внешней области видимости. До закрытия остался всего один шаг!
Давайте еще раз посмотрим на примеры outerFunc ()
и innerFunc ()
:
function outerFunc () {
let outerVar = 'Я снаружи!';
function innerFunc () {
console.
log (externalVar);
}
innerFunc ();}
externalFunc ();
Внутри области innerFunc ()
переменная outerVar
доступна из лексической области.Это уже известно.
Обратите внимание, что вызов innerFunc ()
происходит внутри его лексической области видимости (области видимости outerFunc ()
).
Давайте внесем изменения: innerFunc ()
, который будет вызываться вне его лексической области видимости (за пределами outerFunc ()
). Будет ли innerFunc ()
по-прежнему иметь доступ к externalVar
?
Давайте внесем изменения в фрагмент кода:
function outerFunc () {
let outerVar = 'Я снаружи!';
function innerFunc () {
консоль.журнал (externalVar);
}
return innerFunc;}
const myInnerFunc = externalFunc ();
myInnerFunc ();
Теперь innerFunc ()
выполняется вне своей лексической области видимости. И что важно:
innerFunc ()
по-прежнему имеет доступ к outerVar
из своей лексической области видимости, даже если она выполняется вне ее лексической области.
Другими словами, innerFunc ()
закрывает более (или захватывает, запоминает) переменную outerVar
из своей лексической области видимости.
Другими словами, innerFunc ()
является закрытием , потому что он закрывает переменную outerVar
из своей лексической области видимости.
Вы сделали последний шаг к пониманию того, что такое закрытие:
Замыкание - это функция, которая обращается к своей лексической области видимости, даже если она выполняется вне ее лексической области видимости.
Проще говоря, замыкание - это функция, которая запоминает переменные с того места, где они определены, независимо от того, где они выполняются позже.
Эмпирическое правило для определения замыкания: если вы видите в функции чужеродную переменную (не определенную внутри функции), скорее всего, эта функция является замыканием, потому что чужая переменная захвачена.
В предыдущем фрагменте кода outerVar
- это чужая переменная внутри замыкания innerFunc ()
, захваченная из области outerFunc ()
.
Продолжим примеры, демонстрирующие, почему закрытие полезно.
5.Примеры закрытия
5.1 Обработчик событий
Давайте покажем, сколько раз была нажата кнопка:
пусть countClicked = 0;
myButton.addEventListener ('щелчок', функция handleClick () {
countClicked ++;
myText.innerText = `Вы нажали $ {countClicked} раз`;
});
Откройте демонстрацию и нажмите кнопку. Текст обновится, чтобы показать количество кликов.
При нажатии кнопки handleClick ()
выполняется где-то внутри кода DOM.Казнь происходит далеко от места определения.
Но будучи закрытием, handleClick ()
захватывает countClicked
из лексической области видимости и обновляет его, когда происходит щелчок. Более того, myText
тоже захватывается.
5.2 Обратные вызовы
Захват переменных из лексической области видимости полезен в обратных вызовах.
A setTimeout ()
обратный вызов:
const message = 'Привет, мир!';
setTimeout (функция обратного вызова () {
консоль.журнал (сообщение);
}, 1000);
Обратный вызов ()
является закрытием, потому что он захватывает сообщение переменной .
Функция итератора для forEach ()
:
пусть countEven = 0;
const items = [1, 5, 100, 10];
items.forEach (итератор функции (число) {
if (число% 2 === 0) {
countEven ++;
}
});
countEven;
Итератор
является закрытием, потому что он захватывает переменную countEven
.
5.3 Функциональное программирование
Каррирование происходит, когда функция возвращает другую функцию до тех пор, пока не будут полностью предоставлены аргументы.
Например:
функция умножить (а) {
вернуть функцию executeMultiply (b) {
вернуть a * b;
}
}
const double = умножить (2);
двойной (3);
двойной (5);
const тройной = умножить (3);
тройной (4);
multiply
- каррированная функция, которая возвращает другую функцию.
Каррирование, важная концепция функционального программирования, также возможна благодаря замыканиям.
executeMultiply (b)
- это закрытие, которое захватывает и
из своей лексической области видимости. Когда закрытие вызывается, захваченная переменная a
и параметр b
используются для вычисления a * b
.
6. Заключение
Область действия определяет доступность переменных в JavaScript. Это может быть функция или область видимости блока.
Лексическая область видимости позволяет области действия функции получать статический доступ к переменным из внешних областей видимости.
Наконец, замыкание - это функция, которая захватывает переменные из своей лексической области видимости. Проще говоря, замыкание запоминает переменные из того места, где оно определено, независимо от того, где оно выполняется.
Замыкания захватывают переменные внутри обработчиков событий, обратных вызовов. Они используются в функциональном программировании. Более того, вас могут спросить, как работает закрытие во время собеседования с Frontend.
Каждый разработчик JavaScript должен знать, как работают замыкания. Разобраться с этим ⌐ ■ _ ■.
А как насчет испытания? 7 вопросов для собеседования по закрытию JavaScript. Сможете ли вы им ответить?
Вам все еще сложно понять замыкания? Если да, задайте мне вопрос в комментарии ниже!
Замыкания (программирование) - Полезно
Это касается концепции программирования, а не одного из математических замыканий, физиологических замыканий или любого другого вида.
Замыкания достаточно легко определить технически.
Скажем, Фолдок говорит, что «замыкание - это структура данных, которая содержит выражение и среду привязок переменных, в которой это выражение должно быть вычислено.Переменные могут быть локальными или глобальными ».
... но сложнее интуитивно объяснить или объяснить, почему и когда они интересны. Так:
Говоря чуть более обыденным языком, замыкания часто означают функцию, которая запоминает (часть) области видимости, из которой она возникла, в которой она была определена.
Рассмотрим следующий псевдокод:
var add = function (a) { return function (b) { вернуть a + b; } } var add10 = добавить (10); //Проверь это: add10 (5); // 15, ураОбратите внимание, что в первых пяти строках
a
определены в области внешней функции. Внутренняя функция получает к ней доступ, потому что локальные / вложенные функции получают свою собственную область видимости блока, но также могут видеть область видимости своего родителя. Это половина причины, по которой такие функции полезны.
Но, по-видимому, это язык, который позволяет вам возвращать функции.
Затем им нужно иметь дело с возможностью того, что внутренняя функция может быть передана извне - как мы это сделали. Что это будет делать с задействованными областями действия, в частности, после того, как вы уберете эту внутреннюю функцию, что такое?
На языке, который допускает вложенные функции, но не закрытие, вероятно, сочтет этот недопустимый код, (точные причины могут немного отличаться в зависимости от того, как эти языки думают о сфере действия).
Есть несколько разных вариантов, и их полезно проработать, потому что это также заставляет задуматься о том, как область видимости работает в простых моделях исполнения по сравнению с более гибкими.
В последнем случае одним из наиболее полезных является утверждение, что все, что находится в области действия, когда было определеноadd
(и упоминалось в этой внутренней функции), будет доступно, когда вы вызоветеadd
из любого другого места.
Использование JS / DOM
Использование Python
Замыкания существуют в python с 2.2.
Он позволяет сортировать структуру «массив в массиве» по произвольным столбцам,
без необходимости жесткого кодирования для каждого конкретного случая.
# То есть, если у вас есть помощник, определенный как: def nth_lex (n): вернуть лямбда a, b: cmp (a [n], b [n])
# ... тогда он позволяет: matrixish.sort (nth_lex (2)) # Вместо того, чтобы писать: matrixish.sort (лямбда a, b: cmp (a [2], b [2]))
Есть несколько предостережений, которые не позволяют использовать некоторые из возможных применений.
Основной из них - то, что прилагаемая область доступна только для чтения.Например, в:
def foo (): х = 'а' def bar (): x = 'b' бар() печать x
... вы можете ожидать, что он назначит и, следовательно, напечатает 'b', но область действия bar - foo и доступна только для чтения.
Вы можете ожидать, что он либо запустится во время выполнения, либо создаст переменную, локальную для и . Оказывается, последнее. Но это не то, чего вы, вероятно, хотели бы.
Вы можете использовать замыкания Python для поддержки синтаксического сахара, например:
def оплачено_more_than (сумма): сумма + = 10 #...cheapskate взлом вернуть лямбда-элемент: элемент ['зарплата']> сумма ярмарка = pay_more_than (140) дорого = pay_more_than (160) сотрудники = [{'salary': 150}, {'salary': 170, 'isManager': True}, {'salary': 120}] фильтр печати (ярмарка, сотрудники) фильтр печати (дорого, сотрудники)
справедливых и дорогих - это недавно созданные функции, и их переменная «сумма» основана на значении, которое она имела в определяющем контексте, то есть соответствующих выполнениях pay_more_than.
Это делает их независимыми (без побочных эффектов, например.грамм. внесение изменений в сумму не допускается).
Это также означает, что вы можете использовать их в генераторах списков и функциях:
result = [e for e в служащих, если справедливо (e)]
Обратите внимание на то, что fair (e) более или менее похож на выражение pay_more_than (140) (e), за исключением того, что при каждом выполнении будет создаваться закрывающая функция.