← Назад к вопросам
Есть ли доступ к переменной функции у функции внутри нее?
1.0 Junior🔥 181 комментариев
#JavaScript Core
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Доступ к переменным функции из вложенных функций
Да, полностью есть доступ. Вложенная функция имеет доступ ко всем переменным родительской функции. Это называется замыканием (closure) - один из самых важных концептов в JavaScript.
Базовый пример
function outer() {
const outerVar = 'Я из outer';
const outerNumber = 42;
function inner() {
// ✅ Полный доступ к переменным outer
console.log(outerVar); // "Я из outer"
console.log(outerNumber); // 42
const innerVar = 'Я из inner';
console.log(innerVar); // "Я из inner"
}
inner(); // Вызываем
}
outer();
// Вывод:
// Я из outer
// 42
// Я из inner
Как работает область видимости
function outer() {
const level1 = 'L1';
function middle() {
const level2 = 'L2';
function inner() {
const level3 = 'L3';
// inner имеет доступ к:
console.log(level1); // ✅ L1 (из outer)
console.log(level2); // ✅ L2 (из middle)
console.log(level3); // ✅ L3 (собственная)
}
inner();
// console.log(level3); // ❌ Ошибка! Нет доступа к level3
}
middle();
// console.log(level2); // ❌ Ошибка! Нет доступа к level2
}
outer();
Замыкания (Closures)
Замыкание - это когда вложенная функция "запоминает" переменные родителя даже после того, как родительская функция закончилась:
function createCounter() {
let count = 0; // Эта переменная будет "запомнена"
function increment() {
count++;
return count;
}
function decrement() {
count--;
return count;
}
// Возвращаем обе функции
return { increment, decrement };
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.decrement()); // 1
// Переменная count остаётся в памяти!
// Она "закрыта" (замкнута) внутри этих функций
Практические примеры замыканий
1. Приватные переменные
function createBank() {
let balance = 0; // Приватная переменная
return {
deposit(amount) {
balance += amount;
return balance;
},
withdraw(amount) {
if (amount <= balance) {
balance -= amount;
return balance;
}
return 'Недостаточно средств';
},
getBalance() {
return balance;
}
};
}
const account = createBank();
console.log(account.deposit(100)); // 100
console.log(account.withdraw(30)); // 70
console.log(account.getBalance()); // 70
// ❌ Нельзя напрямую изменить balance
// account.balance = -1000; // Не сработает
// account.balance всегда undefined
2. Фабрика функций с параметрами
function createMultiplier(multiplier) {
// multiplier будет в замыкании
return function(number) {
return number * multiplier;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
// Каждая функция помнит свой multiplier
3. Debounce и Throttle
// Debounce использует замыкания
function debounce(func, delay) {
let timeoutId = null; // Переменная в замыкании
return function(...args) {
// Вложенная функция имеет доступ к timeoutId
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func(...args), delay);
};
}
const handleSearch = debounce((query) => {
console.log('Ищем:', query);
}, 500);
// Вызываем несколько раз
handleSearch('java'); // timeoutId запоминается
handleSearch('javascript'); // старый timeoutId отменяется
// Через 500ms: "Ищем: javascript"
4. React Hooks используют замыкания
function useCounter(initialValue = 0) {
const [count, setCount] = useState(initialValue);
// Эта функция имеет доступ к count и setCount
// через замыкание
const increment = useCallback(() => {
setCount(prev => prev + 1); // Доступ к prev!
}, []);
return { count, increment };
}
function App() {
const counter = useCounter(10);
return (
<div>
<p>Count: {counter.count}</p>
<button onClick={counter.increment}>Increment</button>
</div>
);
}
Типичные ошибки с замыканиями
Ошибка 1: Переменная в цикле
// ❌ Неправильно - все функции указывают на одну i
const functions = [];
for (var i = 0; i < 3; i++) {
functions.push(function() {
console.log(i); // i изменяется в цикле
});
}
functions[0](); // 3 (не 0!)
functions[1](); // 3 (не 1!)
functions[2](); // 3 (не 2!)
// ✅ Правильно - используем let (block scope)
const functions2 = [];
for (let i = 0; i < 3; i++) {
functions2.push(function() {
console.log(i); // Каждая функция помнит свою i
});
}
functions2[0](); // 0
functions2[1](); // 1
functions2[2](); // 2
// ✅ Альтернатива - IIFE (Immediately Invoked Function Expression)
const functions3 = [];
for (var i = 0; i < 3; i++) {
(function(j) {
functions3.push(function() {
console.log(j); // Каждая функция помнит свою j
});
})(i); // Передаём текущее i как j
}
functions3[0](); // 0
functions3[1](); // 1
functions3[2](); // 2
Ошибка 2: Утечка памяти
// ❌ Проблема - функция занимает память
function createLargeData() {
const largeArray = new Array(1000000).fill('data');
return function() {
// largeArray остаётся в памяти
// даже если она не используется
console.log('Функция создана');
};
}
const fn = createLargeData();
// largeArray всё ещё в памяти! (Утечка)
// ✅ Правильно
function createSmall() {
// Большие данные здесь не нужны
return function() {
console.log('Готово');
};
}
const fn2 = createSmall(); // Нет утечки
Визуализация замыкания
function outer(x) {
// Замыкание будет содержать: { x, inner }
function inner(y) {
return x + y; // Доступ к x из outer
}
return inner;
}
const add5 = outer(5);
console.log(add5(3)); // 8
// В памяти хранится:
// {
// [Function: add5],
// closure: {
// x: 5,
// inner: [Function]
// }
// }
Практический пример: Автентификация
function createAuthService(apiKey) {
// apiKey в замыкании
function makeRequest(endpoint) {
// Доступ к apiKey
return fetch(endpoint, {
headers: { 'Authorization': `Bearer ${apiKey}` }
});
}
function login(credentials) {
return makeRequest('/api/login');
}
function logout() {
return makeRequest('/api/logout');
}
return { login, logout };
}
const auth = createAuthService(process.env.API_KEY);
// login и logout помнят apiKey
// и используют его при каждом вызове
await auth.login({ email: 'user@example.com', password: '123' });
Заключение
Вложенная функция имеет полный доступ к переменным родительской функции. Это создаёт замыкания - один из самых мощных механизмов JavaScript.
Замыкания используются для:
- Создания приватных переменных
- Фабрик функций
- Реализации декораторов
- React Hooks
- Debounce/Throttle
Это критичное знание для любого JavaScript разработчика!