← Назад к вопросам

Есть ли доступ к переменной функции у функции внутри нее?

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 разработчика!