Є думка, що в джаваскрипт немає приватних властивостей. Але це не зовсім так, тому що у 2020 році є механізми закриття доступу до змінної в об'єкті.
Варіант старий, умовно-декларативний, коли ми усно домовляємося не використовувати прямий доступ до змінної, для цього позначаємо її підкресленням:
const obj1 = {
_nameValue: '', // оголошуємо (суто для розробників) властивість приватною
set name(value) {
this._nameValue = value;
},
get name() {
return this._nameValue;
}
};
obj1.name = 'Vasia Pupkin';
console.log(obj1.name); // Vasia Pupkin
obj1._nameValue = 'я рукожоп'; // рукожопство, бо ми попереджали, що властивість є приватною і так не тре робити
console.log(obj1.name); // я рукожоп
Варіант з дискриптором даних, коли ми прописуємо змінній якесь значення і забороняємо цю змінну міняти. Але такий варіант не має сенсу, бо для цього є ключове слово const:
const obj2 = {};
Object.defineProperty(
obj2,
'name',
{
value: 'Вася Пупкін',
}
);
console.log(obj2.name) // Вася Пупкін
obj2.name = 'Іван Петров';
console.log(obj2.name) // все одно буде Вася Пупкін
Є сенс у такій конструкції, коли ми використовуємо сеттер і геттер без створення окремої властивості, назва сеттера/геттера і буде цією властивістю. На перший погляд може здатися, що це просто властивість name, але це не так, оскільки у сеттері та геттері можна писати умови, або ж навіть не використовувати чи сеттер чи геттер:
const obj3 = {};
Object.defineProperty(
obj3,
'name',
{
get() {
return name;
},
set(value) {
name = value;
}
}
);
obj3.name = 'cat';
console.log('obj3: ', obj3.name)
Недолік Object.defineProperty() у тому, що неможливо створити властивість, а потім додати до неї сеттер і геттер. Здавалося б логічним є такий код але він працювати не буде:
const notWork = {};
Object.defineProperty(
notWork,
'name',
{
value: 'Якийсь ноунейм',
configurable: true // ugrade 2021: якщо поставити writable, то працюватиме, але це не зовсім те, оскільки прямий перезапис
}
);
Object.defineProperty(
notWork,
'named',
{
get(){
return this.name;
},
set(value){
this.name = value;
}
}
);
notWork.named = 'Перейменували ноунейма?';
console.log(notWork.named) // Якийсь ноунейм
І саме для цього існує метод Object.defineProperties():
const obj4 = {};
Object.defineProperties(
obj4,
{
'name':
{
value: 'Якийсь Ноунейм',
configurable: true // можливість модифікувати (але не міняти безпосередньо)
},
'named':
{
get() {
return this.name;
},
set(value) {
Object.defineProperty(
this, // звертаємося до поточного об'єкта
'name', // властивість 'name'
{value} // встановлюємо значення {value: value}
);
}
}
}
);
console.log(obj4.named) // Якийсь Ноунейм
obj4.named = 'Нове імя для Ноунейма';
console.log(obj4.named) // Нове імя для Ноунейма
obj4.name = 'Тестова заміна імені';
console.log(obj4.named) // Нове імя для Ноунейма
Апгрейд 2025:
Вік живи, вік учись і дурнем помреш, тому що можна модифікувати код з використанням суто Object.defineProperty() і код з кількома Object.defineProperty() буде працювати. А саме:
const work2025 = {}
Object.defineProperty(
work2025,
'name',
{
value: 'Якийсь ноунейм',
configurable: true
}
)
Object.defineProperty(
work2025,
'named',
{
get(){
return this.name
},
set(value){
Object.defineProperty(work2025, 'name', { value } )
}
}
)
work2025.named = 'Перейменували ноунейма'
console.log(work2025.named) // Перейменували ноунейма
У проєкті є ще спеціальний синтаксис для private властивостей, але на разі працює тільки на webkit'ах.
Апгрейд 2025: у фаєрфоксі теж працює!
const obj5 = class {
#private = 'якесь значення';
getPrivate() {
return this.#private;
}
setPrivate(value) {
this.#private = value;
}
}
const privateTest = new obj5();
console.log(privateTest.getPrivate()) // якесь значення
privateTest.setPrivate('встановлюємо нове значення');
console.log(privateTest.getPrivate()) // встановлюємо нове значення
І кілька слів про дескриптори. Їх є шість і по дефолту вони всі false. Себто якщо якийсь дескриптор нам потрібен, необхідно задати йому значення true.
- value: 'якесь значення'
- writable: true, // чи можна міняти безпосередньо звертаючись до значення
- enumerable: true, // чи можна юзати у циклі for-in та Object.key(), тобто "бачити" властивість у переборі
- configurable: true, // чи можна модифікувати/видаляти
- set(), // сеттер, встановлення якогось значення
- get() // геттер, отримання (повернення) якогось значення
Примітка: не можна використовувати value i writable разом з set() i get()!
Апгрейд 2025: якщо властивість була створена безпосередньо в літералі об'єкта, то вона стає true, її примусово потрібно робити false.
const human = {
age: 42, // початкове значення
name: 'John'
}
Object.defineProperty(human, 'age', {
value: 44, // змінили на 44
enumerable: false,
writable: false,
configurable: false
})
human.age = 43 // не спрацює, значення 44