Suggest an editImprove this articleRefine the answer for “Getters and setters in a class”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**A getter (`get`) and a setter (`set`) are class methods that run automatically when a property is read or written, that is, smart properties with plain field syntax.** Under the hood they are an accessor descriptor (`Object.defineProperty` with `get` and `set` functions), so they store no value themselves but control it: validating input, computing a result on the fly, giving controlled access to `#field` private data. With only a getter defined, the property is effectively read only. ```javascript class Account { #balance = 0; get balance() { return this.#balance; // runs when acc.balance is read } set balance(value) { if (value < 0) throw new Error('Balance cannot be negative'); this.#balance = value; // runs when acc.balance = ... is assigned } } ``` **Key point:** a getter and a setter look like a field but execute code, which buys you logic, validation and computed values without changing the class API.Shown above the full answer for quick recall.Answer (EN)Image**A getter and a setter are a pair of special class methods that run automatically on a read (`obj.prop`) and on an assignment (`obj.prop = value`).** From the outside the property looks like an ordinary field, but there is code behind it that can validate, compute or log. ## Theory ### TL;DR - `get name()` fires when `obj.name` is read, `set name(value)` fires when `obj.name = value` is assigned. - It is an accessor property: no value is stored in it, the value is computed or delegated to another field. - Getter only means a read only property; setter only means a write only property (reading returns `undefined`). - They pair perfectly with `#field` private data: the data is protected and the access is controlled. - The same mechanism is available outside classes through `Object.defineProperty` or an object literal. ### Quick example ```javascript class User { constructor(name) { this._name = name; // a "private" field by convention } get name() { console.log('getter called'); return this._name; } set name(value) { console.log('setter called'); if (value.length < 2) throw new Error('Name is too short'); this._name = value; } } const u = new User('Maria'); console.log(u.name); // 'getter called', then 'Maria' u.name = 'Oleh'; // 'setter called' console.log(u.name); // 'Oleh' ``` The call syntax stays exactly like a plain field: no parentheses, no `getName()`. ### How it works under the hood `get` and `set` in a class body are shorthand for an accessor property descriptor. Formally the class does roughly what `Object.defineProperty` does: ```javascript const user = { _name: 'Maria' }; Object.defineProperty(user, 'name', { get() { return this._name.toUpperCase(); }, set(value) { this._name = value.trim(); } }); user.name = ' oleh '; console.log(user.name); // 'OLEH' ``` Important consequences: - The accessor lives on the class **prototype**, not on the instance, so it is not duplicated in every object. - It is **non enumerable** (`enumerable: false`), so it does not appear in `Object.keys(instance)` or in the `JSON.stringify(instance)` output. If a computed value must be in the JSON, add a `toJSON()` method. - The descriptor can be inspected: `Object.getOwnPropertyDescriptor(User.prototype, 'name')` returns an object with `get` and `set` fields. - An accessor can be static: `static get version() { return '1.0'; }` is read as `User.version`. ### Getter without a setter, and setter without a getter With only a getter defined, the property becomes read only. It is typically used for computed values: `area`, `fullName`, `priceWithTax`. ```javascript class Circle { constructor(radius) { this.radius = radius; } get area() { return Math.PI * this.radius ** 2; } } const c = new Circle(10); console.log(c.area); // 314.159... c.area = 100; // in a module or in strict mode: TypeError console.log(c.area); // 314.159..., the value did not change ``` > In sloppy mode such an assignment is silently ignored, while in modules and in strict mode it throws `TypeError: Cannot set property area`. Class code is always strict, but the assignment line itself runs in its own context, so the behaviour depends on that context. The mirror case, a setter without a getter, is handy when you only need to react to a write and recompute internal data. ```javascript class Temperature { set celsius(value) { this.fahrenheit = value * 1.8 + 32; } } const t = new Temperature(); t.celsius = 25; console.log(t.fahrenheit); // 77 console.log(t.celsius); // undefined, there is no getter ``` ### Working with private fields Most often a getter and a setter are a facade over a `#field` private property. The data is unreachable from outside, but the class decides how to expose it and what to accept. ```javascript class Account { #balance = 0; get balance() { return this.#balance; } set balance(value) { if (value < 0) throw new Error('Balance cannot be negative'); this.#balance = value; } } const acc = new Account(); acc.balance = 100; console.log(acc.balance); // 100 // acc.balance = -50; // Error: Balance cannot be negative ``` This gives you the main benefit of accessors: the public contract (`acc.balance`) stays stable while the internal storage can change at any time. ### Computed properties A getter together with a setter lets you split a composite value into parts and assemble it back. ```javascript class User { constructor(first, last) { this.first = first; this.last = last; } get fullName() { return `${this.first} ${this.last}`; } set fullName(value) { const [first, last] = value.split(' '); this.first = first; this.last = last; } } const u = new User('Maria', 'Koval'); console.log(u.fullName); // 'Maria Koval' u.fullName = 'Oleh Shevchenko'; console.log(u.first); // 'Oleh' console.log(u.last); // 'Shevchenko' ``` The `fullName` property does not exist as a field in memory: it is recomputed from `first` and `last` every time, so the data can never drift out of sync. ### Comparison with a plain property | Feature | Plain property | Getter or setter | | --- | --- | --- | | Stores a value | yes | no, it computes or delegates | | Runs code on access | no | yes, automatically | | Can add validation or logic | no | yes | | Works with private fields | yes | yes | | Access syntax | `obj.prop` | `obj.prop`, identical | | Lives on the instance | yes | on the class prototype | | Included in `JSON.stringify` | yes | no | ### Common mistakes - **Calling it like a method.** `u.name()` throws `TypeError: u.name is not a function`, because a getter returns a value, not a function. - **Infinite recursion.** Writing `this.name = value` inside `set name(value)` makes the setter call itself and blow the stack. You need separate storage: `this._name` or `#name`. - **Same name for the accessor and a field.** `get value()` together with a class field `value = 1` does not work: the instance field shadows the prototype accessor. - **Expecting accessors in JSON.** Computed properties are not serialised, because they are non enumerable and live on the prototype. - **Heavy computation in a getter.** Reading a property looks cheap, so a call inside a loop or a render easily becomes a bottleneck; cache the result. - **Side effects in a getter.** Network requests, state mutations and logging make the code unpredictable: a getter should only return a value.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.