JavaScript Getters and Setters
Learn JavaScript property getters and setters: the get/set syntax, accessor vs data properties, validation, computed properties, and use in classes.
Most object properties in JavaScript are data properties — they simply store a value. But objects also support accessor properties: properties backed by functions that run whenever the property is read or written. These functions are called getters and setters. To the outside world an accessor property looks like an ordinary property (user.age), yet behind the scenes your own code decides what reading or assigning it actually does.
This guide covers the get/set syntax, how accessor properties differ from data properties, the most common use cases (validation and computed values), how getters and setters work inside classes, and the gotchas worth knowing about.
Introduction to Property Getters and Setters
A getter is a function that runs when you read a property; its return value becomes the value of the access. A setter is a function that runs when you assign to a property; it receives the assigned value as its single argument. Because they are functions, they can run validation, compute a result on the fly, log access, or update other properties — all while the calling code uses plain property syntax.
Syntax
Inside an object literal, define a getter with the get keyword followed by a method, and a setter with the set keyword followed by a method that takes one parameter:
let obj = {
get propName() {
// getter, the code executed when obj.propName is read
},
set propName(value) {
// setter, the code executed when obj.propName is written
}
};You may define both, or just one. A property with only a getter is read-only; assigning to it silently does nothing in non-strict mode and throws a TypeError in strict mode. A property with only a setter is write-only — reading it returns undefined.
Accessor properties vs. data properties
It is worth being precise about what kind of property a getter/setter creates. A regular property like width: 5 is a data property with a value. A getter/setter pair instead creates an accessor property that has no value of its own — only get and set functions. The two are mutually exclusive: a single property descriptor cannot have both a value and a get/set.
That is why the validation examples below keep the real number in a separate backing field (_age): the public age accessor needs somewhere to store the data, because it has no value slot of its own. To inspect this directly, see Property flags and descriptors.
Why Use Getters and Setters?
Getters and setters offer several benefits, including:
- Encapsulation: They hide the internal representation behind a stable public interface. You can later change how a value is stored without breaking the code that reads it.
- Validation: You can reject or normalize values in the setter before they are stored.
- Computed Properties: A getter can derive its result from other properties, so the value is always up to date and never stored stale.
- Backward compatibility: If a property that used to be a plain field needs logic added later, you can replace it with an accessor of the same name — calling code does not change.
Practical Examples
Let's dive into some practical examples to illustrate how getters and setters can be used in real-world scenarios.
Example 1: User Object with Age Validation
Consider a user object where we want to ensure that the age property is always within a reasonable range. The setter validates the input; the getter just returns the backing field.
Example 2: Creating Computed Properties
Getters allow us to create properties that are computed on the fly based on other data. Every time area is read it is recalculated, so it stays in sync if width or height change.
Example 3: Defining accessors with Object.defineProperty
The get/set literal syntax is the most common way to declare accessors, but you can also add them to an existing object — including after it was created — with Object.defineProperty. This is handy when the property name is dynamic or when you want to control flags like enumerable.
Best Practices
When using getters and setters, consider the following best practices to ensure your code is clean, maintainable, and efficient:
- Avoid Side Effects in Getters: Getters should be fast and free of side effects, as they are often called implicitly by the engine or during property enumeration.
- Validation: Always validate data in setters to prevent invalid or harmful data from being stored.
- Naming Conventions: Use a leading underscore (
_) for the backing property names to indicate they are private. - JSON Serialization: Note that
JSON.stringify()ignores getters by default. Use a replacer function or explicit serialization if you need to include computed values.
Advanced Use Cases
Dynamic Property Names
JavaScript ES6 introduced computed property names, which can be combined with getters and setters to dynamically define accessor keys based on variables or expressions.
Integrating with Classes
Getters and setters are also highly useful in class-based programming, offering a way to encapsulate and control access to class properties. The syntax is identical — just write get/set methods in the class body — and they are placed on the prototype, so every instance shares them.
Common Pitfalls
A few mistakes catch developers off guard:
- Infinite recursion from a wrong backing field. If a setter for
nameassigns tothis.nameinstead of a separate field likethis._name, the assignment triggers the setter again, forever. Always store the value under a different key. - A getter without a setter is read-only. Assigning to it does nothing (or throws in strict mode), which can look like a silent bug.
JSON.stringify()calls getters but ignores setters. A computed getter is serialized (its return value is included), but there is no way to restore it through a setter onJSON.parse()— you only get back plain data.thisfollows the call site. Inside a getter or setter,thisis the object the property was accessed on. If you copy the accessor onto another object,thischanges accordingly — see Object methods and "this".
The example below shows the recursion trap and its fix:
Conclusion
Mastering property getters and setters is a crucial step towards becoming a proficient JavaScript developer. These features not only enhance the functionality and safety of your code but also pave the way for more readable and maintainable codebases. By following the best practices and examples provided in this guide, developers can effectively utilize getters and setters to their advantage, leading to more robust and efficient JavaScript applications.