Автор оригинала: FreeCodeCamp Community Member.
Закрытие – это сочетание функции и лексической среды (область объема), в пределах которой была объявлена эта функция. Закрытия являются фундаментальным и мощным свойством JavaScript. Эта статья обсуждает «Как» и «почему» о закрытиях:
Пример
//we have an outer function named walk and an inner function named fly
function walk (){
var dist = '1780 feet';
function fly(){
console.log('At '+dist);
}
return fly;
}
var flyFunc = walk(); //calling walk returns the fly function which is being assigned to flyFunc
//you would expect that once the walk function above is run
//you would think that JavaScript has gotten rid of the 'dist' var
flyFunc(); //Logs out 'At 1780 feet'
//but you still can use the function as above
//this is the power of closuresДругой пример
function by(propName) {
return function(a, b) {
return a[propName] - b[propName];
}
}
const person1 = {name: 'joe', height: 72};
const person2 = {name: 'rob', height: 70};
const person3 = {name: 'nicholas', height: 66};
const arr_ = [person1, person2, person3];
const arr_sorted = arr_.sort(by('height')); // [ { name: 'nicholas', height: 66 }, { name: 'rob', height: 70 },{ name: 'joe', height: 72 } ]Закрытие «запоминает» среда, в которой она была создана. Эта среда состоит из любых местных переменных, которые были введены в область примерно на момент закрытия.
function outside(num) {
var rememberedVar = num; // In this example, rememberedVar is the lexical environment that the closure 'remembers'
return function inside() { // This is the function which the closure 'remembers'
console.log(rememberedVar)
}
}
var remember1 = outside(7); // remember1 is now a closure which contains rememberedVar = 7 in its lexical environment, and //the function 'inside'
var remember2 = outside(9); // remember2 is now a closure which contains rememberedVar = 9 in its lexical environment, and //the function 'inside'
remember1(); // This now executes the function 'inside' which console.logs(rememberedVar) => 7
remember2(); // This now executes the function 'inside' which console.logs(rememberedVar) => 9 Закрытия полезны, потому что они позволяют вам «запомнить» данные, а затем позволяют работать с этими данными через возвращенные функции. Это позволяет JavaScript эмулировать частные методы, которые находятся на других языках программирования. Частные методы полезны для ограничения доступа к коду, а также для управления глобальным пространством имен.
Частные переменные и методы
Закрытия также могут быть использованы для инкапсуляции частных данных/методов. Посмотрите на этот пример:
const bankAccount = (initialBalance) => {
const balance = initialBalance;
return {
getBalance: function() {
return balance;
},
deposit: function(amount) {
balance += amount;
return balance;
},
};
};
const account = bankAccount(100);
account.getBalance(); // 100
account.deposit(10); // 110В этом примере мы не сможем получить доступ к Баланс из любой точки за пределами BankAccount Функция, что означает, что мы только что создали частную переменную. Где закрытие? Ну, подумай о том, что BankAccount () возвращается. Он на самом деле возвращает объект с помощью функций внутри него, и все же, когда мы называем Счет. GetBalance () функция может «запомнить» ее первоначальную ссылку на Баланс Отказ Это сила закрытия, где функция «запоминает» ее лексическую область (съемку скомпилирования), даже когда функция выполнена за пределами этого лексического охвата.
Эмулирующие блокирующие переменные.
JavaScript не имел концепции переменных на уровне блоков. Это означает, что при определении переменной внутри петли A для цикла эта переменная также видна изнутри петли для цикла. Так как же может замыкание помочь нам решить эту проблему? Давайте взглянем.
var funcs = [];
for(var i = 0; i < 3; i++){
funcs[i] = function(){
console.log('My value is ' + i); //creating three different functions with different param values.
}
}
for(var j = 0; j < 3; j++){
funcs[j](); // My value is 3
// My value is 3
// My value is 3
}Поскольку переменная у меня нет области блоков, это значение во всех трех функциях была обновлена счетчиком контура и создаваемым вредоносными значениями. Закрытие может помочь нам решить эту проблему, создав снимок окружающей среды, в которой она была создана, сохраняя его состояние.
var funcs = [];
var createFunction = function(val){
return function() {console.log("My value: " + val);};
}
for (var i = 0; i < 3; i++) {
funcs[i] = createFunction(i);
}
for (var j = 0; j < 3; j++) {
funcs[j](); // My value is 0
// My value is 1
// My value is 2
}Поздние версии JavaScript ES6 + имеют новое ключевое слово под названием «Пусть», которое можно использовать для того, чтобы дать переменной блокозам. Существуют также многие функции (Foreach) и целые библиотеки (Loadash.js), которые предназначены для решения таких проблем, как объясненные выше. Они, безусловно, могут повысить вашу производительность, однако остается чрезвычайно важной, чтобы иметь знание всех этих вопросов при попытке создать что-то большое.
Закрытия имеют много специальных приложений, которые полезны при создании больших программ JavaScript.
- Эмуляция частных переменных или инкапсуляции
- Создание асинхронных звонков серверов
- Создание переменных на уровне блоков.
Эмуляция частных переменных.
В отличие от многих других языков, JavaScript не имеет механизма, который позволяет создавать инкапсулированные переменные экземпляра в объекте. Наличие переменных публичных экземпляров может привести к возникновению проблем при строительстве средств к большим программам. Однако с закрытиями эта проблема может быть смягчена.
Как и в предыдущем примере, вы можете создавать функции, которые возвращают объектные литералы с методами, которые имеют доступ к локальным переменным объекта, не подвергая их. Таким образом, делая их эффективно частными.
Закрытия также могут помочь вам управлять глобальным пространством имен, чтобы избежать столкновений с глобальным общим данным. Обычно все глобальные переменные распределяются между всеми сценариями в вашем проекте, что определенно даст вам неприятности при создании средних и крупных программ. Вот почему библиотека и модуль авторы используют замыкание, чтобы скрыть все методы и данные модуля. Это называется шаблоном модуля, он использует немедленно вызываемое выражение функции, которое экспортирует только определенные функциональные возможности для внешнего мира, значительно снижая количество глобальных ссылок.
Вот короткий выборки модуля скелета.
var myModule = (function() = {
let privateVariable = 'I am a private variable';
let method1 = function(){ console.log('I am method 1'); };
let method2 = function(){ console.log('I am method 2, ', privateVariable); };
return {
method1: method1,
method2: method2
}
}());
myModule.method1(); // I am method 1
myModule.method2(); // I am method 2, I am a private variableЗакрытия полезны для захвата новых экземпляров частных переменных, содержащихся в «запоминающейся» среде, и эти переменные могут быть доступны только через возвращенную функцию или методы.
Векторы
Вектор – это, пожалуй, самый простой тип коллекции в Clojure. Вы можете подумать об этом, как массив в JavaScript. Давайте определим простой вектор:
(def a-vector [1 2 3 4 5]) ;; Alternatively, use the vector function: (def another-vector (vector 1 2 3 4 5)) ;; You can use commas to separate items, since Clojure treats them as whitespace. (def comma-vector [1, 2, 3, 4, 5])
Вы увидите, что он использует квадратные скобки, как и массив в JS. Поскольку Clojure, как JS, динамически набирается, векторы могут удерживать элементы любого типа, включая другие векторы.
(def mixed-type-vector [1 "foo" :bar ["spam" 22] #"^baz$"])
Добавление элементов в вектор
Вы можете добавить элементы в вектор, используя конфлиги Отказ Вы также можете предшествовать в списке, используя в , но обратите внимание, что в Предназначен для объединения двух векторов, поэтому и его аргументы должны быть векторами, а использование в медленнее, чем использование конфлиги Отказ
(time (conj [1 2] 3)) ; => "Elapsed time: 0.032206 msecs" ; [1 2 3] (time (into [1] [2 3])) ; => "Elapsed time: 0.078499 msecs" ; [1 2 3]
Идеоон!
Извлечение предметов из вектора
Вы можете извлечь элементы из вектора, используя получить Отказ Это эквивалентно использовать нотацию кронштейна для доступа к предметам в массиве во многих императивных языках. Предметы в векторе 0 – проиндексированы, подсчитывая слева.
var arr = [1, 2, 3, 4, 5]; arr[0]; // => 1
В Clojure это будет написано так:
(def a-vector [1 2 3 4 5]) (get a-vector 0) ; => 1
Вы также можете дать получить Значение по умолчанию, если вы дадите ему индекс, который не находится в массиве.
;; the list doesn't have 2147483647 elements, so it'll return a string instead. (get a-vector 2147483646 "sorry, not found!") ; => "sorry, not found!"
Преобразование других коллекций на векторы
Структуры без векторных данных могут быть преобразованы в векторы, используя VEC функция. С Hashmaps это производит 2D вектор, содержащий пары клавиш и значений.
(vec '(1 2 3 4 5))
; => [1 2 3 4 5]
(vec {:jack "black" :barry "white"})
; => [[:jack "black"] [:barry "white"]]Когда использовать вектор?
Вектор следует использовать практически во всех случаях, если вам нужна коллекция, потому что у них есть самое короткое время в случайных случаях, что позволяет легко извлекать элементы из вектора. Обратите внимание, что векторы заказываются. Если порядок не имеет значения, может быть лучше использовать набор. Также обратите внимание, что векторы предназначены для добавления элементов; Если вам нужно подготовить элементы, вы можете использовать список.
Больше информации о закрытиях:
- Узнайте замыкание JavaScript через шесть минут
- Базовая руководство по закрытиям в JavaScript
- Откройте для себя мощность закрытия в Vuejs
- Закрытия JavaScript объясняются рассылкой пакета
Оригинал: “https://www.freecodecamp.org/news/closures-in-javascript-explained-with-examples/”