Приватные и защищенные свойства и методы

25-08-24 18:59:40


Image for the Приватные и защищенные свойства и методы

Что такое приватные и защищенные свойства и методы?

  1. Приватные свойства и методы: Доступны только внутри класса, в котором они определены. Они не могут быть доступны или изменены извне.
  2. Защищенные свойства и методы: В JavaScript нет нативной поддержки защищенного контроля доступа, как в некоторых других языках (например, Java, C#). Однако существует соглашение об использовании подчеркивания _, чтобы указать, что свойство или метод предназначен для использования только внутри класса и его подклассов. Это лишь соглашение, такие свойства все равно доступны извне.

Приватные свойства и методы с #

В современном JavaScript (с ECMAScript 2020) вы можете создавать по-настоящему приватные свойства и методы с использованием синтаксиса #. Эти свойства и методы строго приватны и не могут быть доступны извне.

class Person {
// Приватное свойство
#age = 0;

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

// Приватный метод
#calculateBirthYear() {
const currentYear = new Date().getFullYear();
return currentYear - this.#age;
}

// Публичный метод
getBirthYear() {
return this.#calculateBirthYear();
}
}

const person = new Person('Алиса', 30);
console.log(person.name); // Вывод: Алиса
console.log(person.getBirthYear()); // Вывод: (текущий год - 30)

console.log(person.#age); // Ошибка: Приватное поле '#age' должно быть объявлено в содержащем классе
console.log(person.#calculateBirthYear()); // Ошибка: Приватное поле '#calculateBirthYear' должно быть объявлено в содержащем классе

В этом примере:

  • Свойство #age и метод #calculateBirthYear являются приватными. Они доступны только внутри класса Person.
  • Попытка получить доступ к ним напрямую извне приводит к ошибке.

Эмуляция защищенных свойств и методов с использованием соглашений

Как упоминалось ранее, в JavaScript нет нативной поддержки защищенных свойств и методов. Однако разработчики часто используют соглашение о наименовании (с использованием _), чтобы указать, что свойство или метод предназначены для использования как защищенные.

class Vehicle {
// Защищенное (по соглашению) свойство
_fuelLevel = 100;

constructor(make, model) {
this.make = make;
this.model = model;
}

// Защищенный (по соглашению) метод
_consumeFuel(amount) {
this._fuelLevel -= amount;
}

drive() {
if (this._fuelLevel > 0) {
this._consumeFuel(10);
console.log(`${this.make} ${this.model} едет.`);
} else {
console.log(`${this.make} ${this.model} закончился бензин.`);
}
}
}

class ElectricCar extends Vehicle {
charge() {
this._fuelLevel = 100; // Прямой доступ к "защищенному" свойству
console.log(`${this.make} ${this.model} полностью заряжен.`);
}
}

const car = new ElectricCar('Tesla', 'Model 3');
car.drive(); // Вывод: Tesla Model 3 едет.
car.charge(); // Вывод: Tesla Model 3 полностью заряжен.
console.log(car._fuelLevel); // Вывод: 100 (но должно быть рассматриваемо как "защищенное")

В этом примере:

  • Свойство _fuelLevel и метод _consumeFuel предназначены для использования как "защищенные" и доступны внутри класса Vehicle и его подкласса ElectricCar.
  • Хотя это соглашение широко используется, следует помнить, что эти свойства остаются доступными извне и должны использоваться с осторожностью.

Различия между приватными и защищенными свойствами/методами

Приватные (#):

  • Настоящая приватная область видимости.
  • Не могут быть доступны извне или из подклассов.
  • Применяются на уровне языка, обеспечивая более строгую инкапсуляцию.

Защищенные (по соглашению, _):

  • Не по-настоящему защищены; это лишь соглашение о наименовании.
  • Доступны извне, хотя предполагается, что они должны рассматриваться как защищенные.
  • Могут быть использованы в подклассах.

Комбинирование приватного и защищенного контроля доступа

Вы можете комбинировать приватные и защищенные методы, чтобы добиться баланса между строгой инкапсуляцией и контролируемым доступом в подклассах.

class BankAccount {
// Приватное свойство
#balance = 0;

constructor(accountHolder, initialDeposit) {
this.accountHolder = accountHolder;
this.#balance = initialDeposit;
}

// Защищенный (по соглашению) метод
_logTransaction(amount, type) {
console.log(`Транзакция: ${type} на сумму $${amount} для ${this.accountHolder}`);
}

// Публичный метод
deposit(amount) {
this.#balance += amount;
this._logTransaction(amount, 'Депозит');
}

withdraw(amount) {
if (this.#balance >= amount) {
this.#balance -= amount;
this._logTransaction(amount, 'Снятие');
} else {
console.log('Недостаточно средств.');
}
}

getBalance() {
return this.#balance;
}
}

const account = new BankAccount('Джон Доу', 1000);
account.deposit(500); // Вывод: Транзакция: Депозит на сумму $500 для Джон Доу
account.withdraw(300); // Вывод: Транзакция: Снятие на сумму $300 для Джон Доу
console.log(account.getBalance()); // Вывод: 1200

console.log(account.#balance); // Ошибка: Приватное поле '#balance' должно быть объявлено в содержащем классе
console.log(account._logTransaction(100, 'Проверка')); // Не рекомендуется, хотя технически возможно

В этом примере:

  • Свойство #balance является приватным, что гарантирует, что оно не может быть напрямую доступно или изменено извне.
  • Метод _logTransaction предназначен для использования как "защищенный" и доступен внутри класса и его подклассов.