Приватні властивості класу
Приватна властивість, це властивість класу, ім'я якої починається з #: вона доступна тільки всередині самого класу, а ззовні повністю недоступна і навіть не видима. На відміну від старої угоди з підкресленням, це гарантія рівня мови, а не домовленість між розробниками.
Теорія
TL;DR
- Синтаксис:
#fieldу тілі класу, доступ черезthis.#field. - Ззовні
obj.#field, цеSyntaxError, а неundefined. - Приватними бувають і поля, і методи, і статичні члени (
static #token). - Приватні поля не успадковуються: нащадок не бачить приватних полів батька.
- Вони не перелічуються у
for...in,Object.keysі не потрапляють уJSON.stringify. - Ім'я приватного поля не можна обчислити динамічно, воно завжди жорстко записане в коді.
Швидкий приклад
class User {
#password = '12345'; // приватна властивість
checkPassword(pwd) {
return pwd === this.#password;
}
}
const u = new User();
console.log(u.#password); // SyntaxError, напряму звернутися не можна
console.log(u.password); // undefined
console.log(u.checkPassword('12345')); // trueТут:
#password, це приватна властивість,- вона доступна тільки всередині методів класу,
- ззовні спроба звернення дає синтаксичну помилку, а не просто
undefined.
Приватні поля й методи
Ховати можна не лише властивості, а й методи:
class Account {
#balance = 0;
#log(message) {
console.log('[LOG]:', message);
}
deposit(amount) {
this.#balance += amount;
this.#log(`Баланс поповнено на ${amount}`);
}
getBalance() {
return this.#balance;
}
}
const acc = new Account();
acc.deposit(100);
// acc.#log('test'); // Помилка, це приватний метод
console.log(acc.getBalance()); // 100Приватні методи зручні для допоміжної логіки, яку не треба показувати користувачеві класу.
Відмінність від звичайних властивостей:
| Властивість | Видимість | Можна змінити ззовні? | Перелічується у for...in? |
|---|---|---|---|
this.value | Публічна | Так | Так |
#value | Приватна | Ні | Ні |
Чому угода з підкресленням не підходить
До появи # розробники імітували приватність угодою: ставили _ перед іменем.
class User {
_password = '12345';
}Але це нічого не захищає:
const u = new User();
u._password = 'hacked'; // спокійно змінюється#, це справжній захист на рівні мови, а не просто домовленість між людьми в команді.
Успадкування та приватна статика
Приватні поля не успадковуються і недоступні в підкласах:
class Parent {
#secret = 'parent secret';
reveal() {
console.log(this.#secret);
}
}
class Child extends Parent {
test() {
console.log(this.#secret); // Помилка, це приватне поле батька
}
}Кожен клас бачить тільки свої приватні поля, навіть якщо наслідує інший.
Статичні члени теж можуть бути приватними:
class Config {
static #token = 'abc123';
static getToken() {
return this.#token;
}
}
console.log(Config.getToken()); // 'abc123'
console.log(Config.#token); // ПомилкаЦе корисно, коли треба сховати конфіденційні дані рівня класу.
Реальний приклад і обмеження
Приватне поле #balance захищає дані від прямого втручання:
class BankAccount {
#balance = 0;
deposit(amount) {
if (amount <= 0) throw new Error('Сума має бути додатною');
this.#balance += amount;
}
withdraw(amount) {
if (amount > this.#balance) throw new Error('Недостатньо коштів');
this.#balance -= amount;
}
getBalance() {
return this.#balance;
}
}
const acc = new BankAccount();
acc.deposit(500);
console.log(acc.getBalance()); // 500
acc.withdraw(200);
console.log(acc.getBalance()); // 300
// acc.#balance = 100000; // Помилка, ззовні змінити не можнаВажливе обмеження: приватну властивість не можна створити динамічно. Імена приватних полів жорстко задані в коді:
const name = '#secret';
class A {
[name] = 123; // Помилка, змінну тут використати не можна
}Так зроблено заради повної гарантії доступу: дістатися до поля можна лише через літерал з #.
Підсумкова таблиця:
| Особливість | Приватна властивість (#field) |
|---|---|
| Видимість | Тільки всередині класу |
| Оголошення | Через #ім'я |
| Доступ ззовні | Помилка, а не просто undefined |
| Успадкування | Не передається нащадкам |
| Можна в статиці | Так (static #secret) |
| Поєднується з геттерами та сетерами | Так |
| Створити динамічно | Ні |
Типові помилки
- Плутати
#fieldі_field. Підкреслення, це лише угода, будь-який код може перезаписати таке поле. - Очікувати
undefinedззовні. Звернення доobj.#fieldпоза класом, це помилка ще на етапі парсингу, тому вона ламає весь файл, а не лише цей рядок. - Розраховувати на доступ із нащадка. Приватні поля батька не видно в підкласі; якщо потрібен доступ, використовуйте
protected-подібний патерн через звичайне поле або геттер. - Перевіряти наявність поля через
inна чужому об'єкті без обережності. Для приватних полів працює#field in obj, але тільки всередині класу, який їх оголошує. - Забувати, що приватні поля не серіалізуються.
JSON.stringify(acc)не покаже#balance, тому для збереження стану потрібен явний метод.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.