Istnieją dwa rodzaje właściwości obiektów.
Pierwszym rodzajem są właściwości danych. Wiemy już, jak z nimi pracować. Wszystkie właściwości, których używaliśmy do tej pory, były właściwościami danych.
Drugi rodzaj właściwości jest czymś nowym. Są to właściwości accessor. Są to w zasadzie funkcje, które wykonują się przy pobieraniu i ustawianiu wartości, ale dla zewnętrznego kodu wyglądają jak zwykłe właściwości.
Getters and setters
Właściwości akcesora są reprezentowane przez metody „getter” i „setter”. W literale obiektowym są one oznaczane przez get i set:
let obj = { get propName() { // getter, the code executed on getting obj.propName }, set propName(value) { // setter, the code executed on setting obj.propName = value }};Getter działa, gdy obj.propName jest odczytywany, setter – gdy jest przypisywany.
Na przykład mamy obiekt user z name i surname:
let user = { name: "John", surname: "Smith"};Teraz chcemy dodać właściwość fullName, która powinna być "John Smith". Oczywiście nie chcemy kopiuj-wklej istniejącej informacji, więc możemy ją zaimplementować jako accessor:
let user = { name: "John", surname: "Smith", get fullName() { return `${this.name} ${this.surname}`; }};alert(user.fullName); // John SmithZ zewnątrz właściwość accessor wygląda jak zwykła. Na tym właśnie polega idea właściwości accessor. Nie wywołujemy user.fullName jako funkcji, tylko czytamy ją normalnie: getter działa za kulisami.
Jak na razie, fullName ma tylko getter. Jeśli spróbujemy przypisać user.fullName=, pojawi się błąd:
let user = { get fullName() { return `...`; }};user.fullName = "Test"; // Error (property has only a getter)Poprawmy to, dodając setter dla user.fullName:
let user = { name: "John", surname: "Smith", get fullName() { return `${this.name} ${this.surname}`; }, set fullName(value) { = value.split(" "); }};// set fullName is executed with the given value.user.fullName = "Alice Cooper";alert(user.name); // Alicealert(user.surname); // CooperW rezultacie mamy „wirtualną” właściwość fullName. Jest ona odczytywalna i zapisywalna.
Deskryptory dla właściwości accessor
Deskryptory dla właściwości accessor różnią się od deskryptorów dla właściwości data.
Dla właściwości accessor nie ma value lub writable, ale zamiast tego są funkcje get i set.
To znaczy, deskryptor dostępu może mieć:
-  get– funkcję bez argumentów, która działa, gdy właściwość jest odczytywana,
-  set– funkcję z jednym argumentem, która jest wywoływana, gdy właściwość jest ustawiana,
-  enumerable– tak samo jak dla właściwości danych,
-  configurable– tak samo jak dla właściwości danych.
Na przykład, aby utworzyć accessor fullName z defineProperty, możemy przekazać deskryptor z get i set:
let user = { name: "John", surname: "Smith"};Object.defineProperty(user, 'fullName', { get() { return `${this.name} ${this.surname}`; }, set(value) { = value.split(" "); }});alert(user.fullName); // John Smithfor(let key in user) alert(key); // name, surnameProszę zauważyć, że właściwość może być albo accessorem (ma get/set metod), albo właściwością danych (ma value), a nie obydwoma.
Jeśli spróbujemy dostarczyć zarówno get, jak i value w tym samym deskryptorze, wystąpi błąd:
// Error: Invalid property descriptor.Object.defineProperty({}, 'prop', { get() { return 1 }, value: 2});Smarter getters/setters
Getters/setters mogą być używane jako wrappery nad „prawdziwymi” wartościami właściwości, aby uzyskać większą kontrolę nad operacjami z nimi.
Na przykład, jeśli chcemy zabronić zbyt krótkich nazw dla user, możemy mieć setter name i przechowywać wartość w oddzielnej właściwości _name:
let user = { get name() { return this._name; }, set name(value) { if (value.length < 4) { alert("Name is too short, need at least 4 characters"); return; } this._name = value; }};user.name = "Pete";alert(user.name); // Peteuser.name = ""; // Name is too short...Więc, nazwa jest przechowywana we właściwości _name, a dostęp do niej odbywa się poprzez getter i setter.
Technicznie, kod zewnętrzny jest w stanie uzyskać bezpośredni dostęp do nazwy za pomocą user._name. Ale istnieje powszechnie znana konwencja, że właściwości zaczynające się od podkreślnika "_" są wewnętrzne i nie powinny być dotykane spoza obiektu.
Użycie dla kompatybilności
Jednym z wielkich zastosowań accessorów jest to, że pozwalają one w każdej chwili przejąć kontrolę nad „zwykłą” właściwością danych poprzez zastąpienie jej getterem i setterem i podrasowanie jej zachowania.
Wyobraźmy sobie, że zaczęliśmy implementować obiekty użytkownika używając właściwości danych name i age:
function User(name, age) { this.name = name; this.age = age;}let john = new User("John", 25);alert( john.age ); // 25…Ale prędzej czy później wszystko może się zmienić. Zamiast age możemy zdecydować się na przechowywanie birthday, ponieważ jest to bardziej precyzyjne i wygodne:
function User(name, birthday) { this.name = name; this.birthday = birthday;}let john = new User("John", new Date(1992, 6, 1));A teraz co zrobić ze starym kodem, który nadal używa właściwości age? 
Możemy spróbować znaleźć wszystkie takie miejsca i je naprawić, ale to wymaga czasu i może być trudne do wykonania, jeśli ten kod jest używany przez wiele innych osób. A poza tym age jest miłą rzeczą, którą warto mieć w user, prawda?
Zachowajmy ją.
Dodanie gettera dla age rozwiązuje problem:
function User(name, birthday) { this.name = name; this.birthday = birthday; // age is calculated from the current date and birthday Object.defineProperty(this, "age", { get() { let todayYear = new Date().getFullYear(); return todayYear - this.birthday.getFullYear(); } });}let john = new User("John", new Date(1992, 6, 1));alert( john.birthday ); // birthday is availablealert( john.age ); // ...as well as the ageTeraz stary kod też działa, a my mamy fajną dodatkową właściwość.