Можно ли скрыть только поля при инкапсуляции в JavaScript/Node.js?

«Можно ли скрыть только поля при инкапсуляции в JavaScript/Node.js?» — вопрос из категории ООП, который задают на 26% собеседований Node.js Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В чистом JavaScript (ES6 и выше) нет встроенных модификаторов доступа, таких как private, но есть несколько паттернов для эмуляции приватных полей.

1. Приватные поля класса (современный стандарт, ES2022): Самый чистый и рекомендуемый способ. Поле становится недоступным за пределами класса.

class User {
  #secretKey; // Приватное поле
  name;      // Публичное поле

  constructor(name, key) {
    this.name = name;
    this.#secretKey = key;
  }

  getKeyHash() {
    // Доступ к приватному полю возможен только внутри класса
    return hashFunction(this.#secretKey);
  }
}

const user = new User('Alice', 's3cr3t');
console.log(user.name); // 'Alice'
console.log(user.#secretKey); // SyntaxError: Private field '#secretKey' must be declared in an enclosing class
console.log(user.getKeyHash()); // Работает

2. Замыкания (Closures) в конструкторе функции или фабрике: Классический прием, обеспечивающий настоящую приватность.

function createUser(name) {
  let _password = ''; // Приватная переменная, доступная только через замыкание

  return {
    name: name,
    setPassword: (pw) => {
      _password = pw;
    },
    validatePassword: (pw) => {
      return _password === pw;
    }
  };
}

const user = createUser('Bob');
user.setPassword('123');
console.log(user.validatePassword('123')); // true
console.log(user._password); // undefined

3. Символы (Symbols) или WeakMaps (менее строгие методы): Эти методы скорее защищают от случайного доступа, чем от намеренного.

// С использованием WeakMap
const _privateData = new WeakMap();

class Logger {
  constructor(logLevel) {
    // Сохраняем приватные данные в WeakMap, используя экземпляр как ключ
    _privateData.set(this, { level: logLevel, internalCounter: 0 });
  }

  log(message) {
    const data = _privateData.get(this);
    data.internalCounter++;
    console.log(`[${data.level}] ${message} (call #${data.internalCounter})`);
  }
}

Вывод: Да, скрыть поля можно. Для современного кода на Node.js используйте синтаксис приватных полей класса (#field). Для максимальной изоляции в функциональном стиле подходят замыкания.