Замыкания JavaScript: понимание закрытых переменных, обратных вызовов и мемоизации для эффективного кода
JavaScript - это мощный язык программирования, который используется для создания интерактивных веб-сайтов и веб-приложений. Одним из наиболее важных понятий в JavaScript является замыкание. Поначалу замыкания могут быть немного сложными для понимания, но как только вы освоитесь с ними, они могут сделать ваш код намного более мощным и гибким. В этой статье мы объясним, что такое замыкания, как они работают и как их использовать в вашем коде.
Что такое замыкания?
Замыкание — это функция, которая имеет доступ к переменным своей родительской функции, даже после возврата родительской функции. Другими словами, замыкание создается, когда функция определена внутри другой функции, и внутренняя функция имеет доступ к переменным внешней функции. Это означает, что внутренняя функция «закрывает» переменные внешней функции, отсюда и название «закрытие».
Как работают замыкания?
Чтобы понять, как работают замыкания, давайте рассмотрим пример. Рассмотрим следующий код:
function outer() {
var name = "Hussaini";
function inner() {
console.log(name);
}
return inner;
}
var closure = outer();
closure();
В этом коде мы определяем функцию с именем outer
, которая содержит переменную name
и функцию inner
. Функция inner
записывает значение name
в консоль. Затем мы возвращаем функцию inner из outer и сохраняем ее в переменной с именем closure
. Наконец, мы вызываем функцию closure
.
"Hussaini"
Когда мы вызываем closure()
, он регистрирует значение name
в консоли, даже если name
не определено в самой функции closure
. Это связано с тем, что closure
- это замыкание, которое имеет доступ к переменным outer
функции, даже если outer
уже возвращен.
Это может показаться маленькой и незначительной деталью, но при правильном использовании затворы могут быть невероятно мощными. Они позволяют вам создавать функции, которые являются одновременно гибкими и эффективными, и могут помочь вам написать код, который легче читать и поддерживать.
Как использовать замыкания?
Теперь, когда мы знаем, что такое замыкания и как они работают, давайте рассмотрим несколько примеров того, как их использовать в вашем коде.
1. Создание частных переменных
Одним из распространенных способов использования замыканий является создание закрытых переменных. В JavaScript все переменные, определенные внутри функции, доступны для любых вложенных функций внутри этой функции. Это означает, что если вы определяете переменную в функции, любые функции, определенные внутри этой функции, также могут получить доступ к этой переменной. Например:
function outer() {
var name = "Hussaini";
function inner() {
console.log(name);
}
inner();
}
outer();
В этом коде мы определяем функцию с именем outer
, которая содержит переменную name
и функцию inner
. Функция inner
записывает значение name
в консоль. Затем мы вызываем функцию inner
изнутри функции outer
.
"Hussaini"
Когда мы вызываем outer()
, он записывает значение name
в консоль, потому что inner
имеет доступ к переменной name. Однако это также означает, что любые другие функции, определенные в outer
, также будут иметь доступ к переменной name
, что не всегда может быть желательно.
Чтобы создать закрытую переменную, доступную только inner
функции, мы можем использовать замыкание. Вот пример:
function outer() {
var name = "Hussaini";
function inner() {
console.log(name);
}
return inner;
}
var closure = outer();
closure();
В этом коде мы определяем функцию с именем outer
, которая содержит переменную name
и функцию inner
. Функция inner
возвращается из outer
вместо того, чтобы вызываться напрямую. Затем мы сохраняем возвращаемую функцию в переменной с именем closure
. Наконец, мы вызываем функцию закрытия.
"Hussaini"
Когда мы вызываем closure()
, он записывает значение name
в консоль, как и раньше. Однако, поскольку name
определено внутри функции outer
и недоступно непосредственно за ее пределами, фактически это закрытая переменная, доступ к которой возможен только через замыкание, созданное outer
.
2. Создание функций обратного вызова
Еще одно распространенное использование замыканий — создание функций обратного вызова. Функция обратного вызова — это функция, которая передается в качестве аргумента другой функции и выполняется после завершения исходной функции. Функции обратного вызова можно использовать для различных целей, например для обработки пользовательского ввода или ответа на запросы сервера.
Вот пример того, как создать функцию обратного вызова с использованием замыканий:
function calculate(num1, num2, operation) {
var result = operation(num1, num2);
console.log("Result: " + result);
}
function add(num1, num2) {
return num1 + num2;
}
function subtract(num1, num2) {
return num1 - num2;
}
calculate(5, 10, add);
calculate(5, 10, subtract);
В этом коде мы определяем функцию с именем calculate
, которая принимает три аргумента: num1
, num2
и operation
. Ожидается, что аргументом operation
будет функция, которая принимает два числа и возвращает их результат. Затем мы используем функцию operation
для вычисления результата num1
и num2
и записываем результат в консоль.
Мы также определяем две дополнительные функции, называемые add
и subtract
, которые принимают два числа и возвращают их сумму и разность соответственно. Затем мы дважды вызываем функцию calculate, каждый раз передавая функции add
и subtract
в качестве аргумента операции.
Когда мы запустим этот код, мы должны увидеть следующий вывод:
//Result: 15
//Result: -5
Это связано с тем, что функция calculate
использует функцию operation
, переданную в качестве аргумента для выполнения вычисления. Это позволяет нам использовать разные функции для разных вычислений без необходимости каждый раз переписывать функцию calculate
.
3. Мемоизация
Наконец, замыкания также можно использовать для запоминания, что является методом оптимизации производительности функций путем кэширования результатов дорогостоящих вычислений. Запоминание может быть особенно полезно при работе с рекурсивными функциями, которые выполняют одни и те же вычисления несколько раз.
Вот пример того, как использовать замыкания для запоминания:
function fibonacci() {
var cache = {};
function calculate(n) {
if (n in cache) {
return cache[n];
} else {
if (n <= 1) {
return n;
} else {
var result = calculate(n - 1) + calculate(n - 2);
cache[n] = result;
return result;
}
}
}
return calculate;
}
var fib = fibonacci();
console.log(fib(10));
console.log(fib(15));
console.log(fib(20));
В этом коде мы определяем функцию под названием fibonacci
, которая возвращает другую функцию под названием calculate
. Функция calculate
принимает единственный аргумент n и использует запоминание для вычисления n-го числа Фибоначчи.
Мы используем замыкание для создания объекта кэша, который хранит результаты дорогостоящих вычислений. Если результат вычисления для заданного значения n уже находится в кэше, мы возвращаем этот результат вместо повторного выполнения вычисления. В противном случае мы вычисляем результат, используя рекурсивный вызов для вычисления, сохраняем результат в кэше, а затем возвращаем результат.
Наконец, мы вызываем функцию fibonacci
, чтобы получить функцию вычисления, и сохраняем ее в переменной с именем fib
. Затем мы вызываем функцию fib
три раза с разными значениями, чтобы вычислить 10-е, 15-е и 20-е числа Фибоначчи.
Когда мы запустим этот код, мы должны увидеть следующий вывод:
//55
//610
//6765
Это связано с тем, что функция вычисления использует запоминание, чтобы избежать многократного выполнения одних и тех же вычислений. Это делает функцию более быстрой и эффективной.
Вывод
В заключение отметим, что замыкания - это мощная функция JavaScript, которая позволяет создавать функции с закрытыми переменными и поддерживать состояние между вызовами функций. Замыкания также можно использовать для создания функций обратного вызова и для запоминания. Поняв, как работают замыкания, вы сможете написать более эффективный и гибкий JavaScript-код.