Є думка, що в джаваскрипт немає приватних властивостей. Але це не зовсім так, тому що у 2020 році є механізми закриття доступу до змінної в об'єкті.
Варіант старий, умовно-декларативний, коли ми усно домовляємося не використовувати прямий доступ до змінної, для цього позначаємо її підкресленням:
const obj1 = { _name: '', // оголошуємо (суто для розробників) властивість приватною set name(value) { this._name = value; }, get name() { return this._name; } }; obj1.name = 'Vasia Pupkin'; console.log(obj1.name); // Vasia Pupkin obj1._name = 'я рукожоп'; // рукожопство, бо ми попереджали, що властивість є приватною і так не тре робити 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) // Нове імя для Ноунейма
У проєкті є ще спеціальний синтаксис для private властивостей, але на разі працює тільки на webkit'ах:
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()!