Є думка, що в джаваскрипт немає приватних властивостей. Але це не зовсім так, тому що у 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