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

Как реализуются приватные переменные?

2.0 Middle🔥 151 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Приватные переменные в JavaScript

Приватные переменные — это переменные, доступные только внутри своей области видимости (scope). В JavaScript есть несколько способов реализации приватности, каждый со своими особенностями.

1. Замыкания (Closures) — классический способ

Это самый старый и проверенный способ. Приватная переменная находится внутри функции и недоступна извне:

function createCounter() {
  let count = 0; // приватная переменная

  return {
    increment() {
      return ++count;
    },
    decrement() {
      return --count;
    },
    getCount() {
      return count;
    }
  };
}

const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.count); // undefined — приватна!
console.log(counter.getCount()); // 2 — доступ только через метод

2. Классы с приватными полями (ES2022)

Современный и рекомендуемый способ. Используется символ # перед именем переменной:

class User {
  #password; // приватное поле
  #ssn; // социальный номер
  
  name; // публичное поле

  constructor(name, password, ssn) {
    this.name = name;
    this.#password = password;
    this.#ssn = ssn;
  }

  // Публичный метод для доступа к приватной переменной
  verifyPassword(inputPassword) {
    return inputPassword === this.#password;
  }

  // Приватный метод
  #hashPassword(password) {
    return btoa(password); // простой пример
  }

  // Публичный метод, использующий приватный
  changePassword(oldPassword, newPassword) {
    if (!this.verifyPassword(oldPassword)) {
      throw new Error('Wrong password');
    }
    this.#password = this.#hashPassword(newPassword);
  }
}

const user = new User('John', 'secret123', '123-45-6789');
console.log(user.name); // 'John' — доступна
console.log(user.#password); // SyntaxError! — недоступна
console.log(user.verifyPassword('secret123')); // true — доступ через метод

3. Соглашение об именовании (конвенция)

Используется подчеркивание перед именем, но это не гарантирует приватность:

class BankAccount {
  constructor(balance) {
    this._balance = balance; // "приватная" по соглашению
  }

  getBalance() {
    return this._balance;
  }

  deposit(amount) {
    this._balance += amount;
  }
}

const account = new BankAccount(1000);
console.log(account._balance); // 1000 — доступна! Это просто соглашение
account._balance = 999999; // можно изменить — нарушение инкапсуляции

// Минусы: это не реальная приватность

4. Символы (Symbols) — нестандартный способ

Более хитрый способ с использованием Symbol. Символы уникальны и не видны в Object.keys():

const privateData = Symbol('privateData');

class Service {
  constructor(apiKey) {
    this[privateData] = apiKey; // "приватная" переменная
  }

  getApiKey() {
    return this[privateData];
  }
}

const service = new Service('secret-key-123');
console.log(service[privateData]); // 'secret-key-123' — доступна, если знать символ
console.log(Object.keys(service)); // [] — не видна в Object.keys()
console.log(Object.getOwnPropertySymbols(service)); // [Symbol(privateData)] — но видна так

// Это гарантирует приватность от случайного доступа, но не от специалистов

5. WeakMap для действительной приватности

Используется WeakMap для хранения приватных данных. Невозможно получить данные без ссылки на WeakMap:

const privateData = new WeakMap();

class User {
  constructor(name, email) {
    this.name = name;
    // Храним приватные данные в WeakMap
    privateData.set(this, {
      email: email,
      createdAt: new Date()
    });
  }

  getEmail() {
    return privateData.get(this).email;
  }

  // Нет способа получить email напрямую
}

const user = new User('John', 'john@example.com');
console.log(user.name); // 'John'
console.log(user.email); // undefined
console.log(user.getEmail()); // 'john@example.com'

// Невозможно получить данные из WeakMap без объекта user
// Это полная приватность!

6. Немедленно вызванные функции (IIFE)

Старый способ для создания приватного scope:

const calculator = (() => {
  let result = 0; // приватная переменная

  return {
    add(x) {
      result += x;
      return this;
    },
    multiply(x) {
      result *= x;
      return this;
    },
    getResult() {
      return result;
    }
  };
})();

calculator.add(5).multiply(2).add(3);
console.log(calculator.getResult()); // 13
console.log(calculator.result); // undefined — приватна

Сравнение способов

// Способ 1: Замыкания
const method1 = (() => {
  let private = 'secret';
  return { getPrivate: () => private };
})();
// Плюсы: простой, работает везде
// Минусы: синтаксис громоздкий

// Способ 2: Приватные поля класса (рекомендуется)
class Method2 {
  #private = 'secret';
  getPrivate() { return this.#private; }
}
// Плюсы: понятный синтаксис, явный, современный
// Минусы: только в классах, поддержка с ES2022

// Способ 3: Соглашение
class Method3 {
  _private = 'secret';
  getPrivate() { return this._private; }
}
// Плюсы: работает везде
// Минусы: не реальная приватность

// Способ 4: WeakMap
const method4Data = new WeakMap();
class Method4 {
  constructor() {
    method4Data.set(this, { private: 'secret' });
  }
  getPrivate() { return method4Data.get(this).private; }
}
// Плюсы: полная приватность, память освобождается
// Минусы: сложнее, медленнее

Рекомендация

// В современных проектах используй приватные поля класса
// Это стандарт, понятный и производительный

class MyClass {
  #privateField = 'не доступна снаружи';
  publicField = 'доступна всем';

  #privateMethod() {
    // можно создавать и приватные методы
  }

  publicMethod() {
    return this.#privateField;
  }
}

// Для старых браузеров или особых случаев — замыкания
const createModule = () => {
  let privateVar = 'secret';
  return {
    getSecret: () => privateVar
  };
};

Приватные переменные — это про инкапсуляцию и защиту деталей реализации от неправильного использования.