JavaScript Getters and Setters
Getters and setters are special methods that let you define how properties are accessed and modified. They provide computed properties, validation, and controlled access to your object's data.
Try It Yourself
Experiment with getters and setters to see how they work:
// Getters and Setters in action
const person = {
firstName: "John",
lastName: "Doe",
birthYear: 1990,
// Getter - computed property
get fullName() {
return this.firstName + " " + this.lastName;
},
// Setter - with validation
set fullName(value) {
const parts = value.split(" ");
this.firstName = parts[0] || "";
this.lastName = parts[1] || "";
},
// Getter - calculated value
get age() {
return new Date().getFullYear() - this.birthYear;
}
};
// Using getter (no parentheses!)
console.log("Full name:", person.fullName);
console.log("Age:", person.age);
// Using setter (looks like assignment)
person.fullName = "Jane Smith";
console.log("\nAfter setting fullName:");
console.log("First:", person.firstName);
console.log("Last:", person.lastName);
console.log("Full:", person.fullName);What are Getters?
A getter is a method that returns a value when you access a property. It's defined with the get keyword and accessed without parentheses:
const circle = {
radius: 5,
// Getter - accessed like a property
get diameter() {
return this.radius * 2;
},
get area() {
return Math.PI * this.radius * this.radius;
},
get circumference() {
return 2 * Math.PI * this.radius;
}
};
// Access getters WITHOUT parentheses
console.log(circle.diameter); // 10
console.log(circle.area); // 78.54...
console.log(circle.circumference); // 31.42...
// The getter recalculates when radius changes
circle.radius = 10;
console.log(circle.diameter); // 20What are Setters?
A setter is a method that runs when you assign a value to a property. It's defined with the set keyword:
const user = {
_name: "", // Convention: underscore for "private" property
// Getter
get name() {
return this._name;
},
// Setter with validation
set name(value) {
if (typeof value !== "string") {
throw new Error("Name must be a string");
}
if (value.length < 2) {
throw new Error("Name must be at least 2 characters");
}
this._name = value.trim();
}
};
// Setting triggers the setter
user.name = " Alice "; // Whitespace is trimmed
console.log(user.name); // "Alice"
// Validation prevents bad data
try {
user.name = "A"; // Too short
} catch (e) {
console.log(e.message); // "Name must be at least 2 characters"
}Getter and Setter Pairs
Getters and setters often work together to create smart properties:
const temperature = {
_celsius: 0,
// Getter and setter for Celsius
get celsius() {
return this._celsius;
},
set celsius(value) {
this._celsius = value;
},
// Getter and setter for Fahrenheit (converts automatically)
get fahrenheit() {
return this._celsius * 9/5 + 32;
},
set fahrenheit(value) {
this._celsius = (value - 32) * 5/9;
}
};
// Set in Celsius, read in both
temperature.celsius = 100;
console.log(temperature.celsius); // 100
console.log(temperature.fahrenheit); // 212
// Set in Fahrenheit, read in both
temperature.fahrenheit = 32;
console.log(temperature.celsius); // 0
console.log(temperature.fahrenheit); // 32Getters vs Regular Methods
| Feature | Getter | Regular Method |
|---|---|---|
| Syntax to access | obj.prop | obj.method() |
| Parentheses needed | ||
| Can accept arguments | ||
| Feels like a property | ||
| Best for | Computed values | Actions/operations |
Getters and Setters in Classes
The same syntax works in ES6 classes:
class Rectangle {
constructor(width, height) {
this._width = width;
this._height = height;
}
// Getters
get width() {
return this._width;
}
get height() {
return this._height;
}
get area() {
return this._width * this._height;
}
get perimeter() {
return 2 * (this._width + this._height);
}
// Setters with validation
set width(value) {
if (value <= 0) throw new Error("Width must be positive");
this._width = value;
}
set height(value) {
if (value <= 0) throw new Error("Height must be positive");
this._height = value;
}
}
const rect = new Rectangle(10, 5);
console.log(rect.area); // 50
console.log(rect.perimeter); // 30
rect.width = 20;
console.log(rect.area); // 100Using Object.defineProperty
You can also define getters and setters dynamically:
const obj = {
_value: 0
};
// Define getter/setter using Object.defineProperty
Object.defineProperty(obj, "value", {
get: function() {
console.log("Getting value");
return this._value;
},
set: function(newValue) {
console.log("Setting value to", newValue);
this._value = newValue;
},
enumerable: true,
configurable: true
});
obj.value = 42; // "Setting value to 42"
console.log(obj.value); // "Getting value" then 42
// Define multiple properties at once
Object.defineProperties(obj, {
doubled: {
get() { return this._value * 2; }
},
squared: {
get() { return this._value * this._value; }
}
});
console.log(obj.doubled); // 84
console.log(obj.squared); // 1764Read-Only Properties
A getter without a setter creates a read-only property:
const config = {
_apiKey: "secret-key-123",
// Read-only property (getter only)
get apiKey() {
return this._apiKey;
}
// No setter = read-only
};
console.log(config.apiKey); // "secret-key-123"
config.apiKey = "hacked"; // Silently fails (or throws in strict mode)
console.log(config.apiKey); // Still "secret-key-123"
// Creating truly immutable computed properties
const math = {
get PI() {
return 3.14159265359;
},
get E() {
return 2.71828182846;
}
};
console.log(math.PI); // 3.14159265359
math.PI = 3; // Fails silently
console.log(math.PI); // Still 3.14159265359Lazy Evaluation and Caching
Getters can implement lazy evaluation - computing expensive values only when first accessed:
const data = {
_cache: null,
// Expensive computation - only runs once
get processedData() {
if (this._cache === null) {
console.log("Computing (expensive operation)...");
// Simulate expensive computation
this._cache = Array.from({ length: 1000 }, (_, i) => i * 2);
}
return this._cache;
},
clearCache() {
this._cache = null;
}
};
// First access - computes
console.log(data.processedData.length); // "Computing..." then 1000
// Second access - uses cache
console.log(data.processedData.length); // 1000 (no "Computing...")
// After clearing cache
data.clearCache();
console.log(data.processedData.length); // "Computing..." then 1000With Private Fields (ES2022)
Modern JavaScript supports truly private fields with the # prefix:
// ES2022 private fields with getters/setters
class BankAccount {
#balance = 0; // Private field
get balance() {
return this.#balance;
}
// No setter - balance can only change through methods
deposit(amount) {
if (amount > 0) {
this.#balance += amount;
}
}
withdraw(amount) {
if (amount > 0 && amount <= this.#balance) {
this.#balance -= amount;
return true;
}
return false;
}
}
const account = new BankAccount();
account.deposit(100);
console.log(account.balance); // 100
account.balance = 1000000; // Fails - no setter
console.log(account.balance); // Still 100
// account.#balance = 1000000; // SyntaxError - private fieldTest Your Knowledge
Getters and Setters Quiz
5 questionsHow do you access a getter property?
What happens when you assign to a getter-only property?
What is the purpose of a setter?
Which syntax defines a getter in an object literal?
Why use an underscore prefix like _value?
Coding Challenge
Create a Product class with getters and setters for price, quantity, total, and discount. Include validation to prevent invalid values.
// Challenge: Create a Product class with getters and setters
// Requirements:
// - Private _price and _quantity fields
// - Getters for price, quantity, and total (price * quantity)
// - Setters for price and quantity with validation:
// - Price must be a positive number
// - Quantity must be a non-negative integer
// - A discount getter/setter that adjusts the price by percentage
class Product {
constructor(name, price, quantity) {
// Your code here
}
// Add getters and setters
}
// Test your implementation
const laptop = new Product("Laptop", 1000, 5);
console.log("Price:", laptop.price); // 1000
console.log("Quantity:", laptop.quantity); // 5
console.log("Total:", laptop.total); // 5000
laptop.price = 1200;
console.log("New total:", laptop.total); // 6000
laptop.discount = 10; // 10% off
console.log("After discount:", laptop.price); // 1080
// These should throw errors:
// laptop.price = -100; // Error: must be positive
// laptop.quantity = -1; // Error: must be non-negativeSummary
- Getters (
get) return computed values when a property is accessed - Setters (
set) run code when a property is assigned - Access getters without parentheses like regular properties
- Use setters for validation and data transformation
- A getter without a setter creates a read-only property
- Use underscore prefix (_value) by convention for backing properties
- ES2022 private fields (#value) provide true privacy