In JavaScript, classes allow us to define properties and methods, but sometimes we need fine-grained control over how certain properties behave when they’re accessed or modified. For instance, when someone accesses a password field, we may want to return an encrypted version or hide the value altogether.
This is where getters and setters come into play. They provide a mechanism to control the access to the properties of an object.
Let’s start with a simple example:
class User {
constructor(email, password) {
this.email = email;
this.password = password;
}
}
const newObj = new User("harsh@gmail.com", "abc");
console.log(newObj.password); // Output: abc
In this code, we’ve created a class User
with an email and password property. When we access the password, it returns the value directly.
But what if we don’t want the password to be freely accessible? What if we want to encrypt the password before returning it? Let’s see how we can achieve that.
Using Getters and Setters
With getters and setters, we can control how the password is returned:
class User {
constructor(email, password) {
this.email = email;
this.password = password;
}
get password() {
return this.password.toUpperCase();
}
}
const myObj = new User("harsh@gmail.com", "pass123");
console.log(myObj.password);
This will throw an error as it didn’t even reach the last line of code, it says that if you have defined getter, then you also have to define its setter property.
So here is the code with getter and setter
class User {
constructor(email, password) {
this.email = email;
this.password = password;
}
get password() {
return this.password.toUpperCase();
}
set password(value) {
return this.password = value;
}
}
const myObj = new User("harsh@gmail.com", "pass123");
console.log(myObj.password);
//error: set password [as password]
//error: maximum call stack size exceeded
Again this code won’t run, as we are setting the password inside the setter and constructor, it will throw an error saying “maximum call stack size exceeded“ because, while setting the value it is calling the constructor again and again.
Solving Stack Overflow with Underscore Notation
The solution is simple: use a different internal variable for the password, typically prefixed with an underscore (_
), to indicate that it’s a private property.
class User {
constructor(email, password) {
this._email = email;
this._password = password;
}
get password() {
return this._password.toUpperCase(); // Or return an encrypted version
}
set password(value) {
this._password = value;
}
}
const myObj = new User("harsh@gmail.com", "abc");
console.log(myObj.password); // Output: ABC
// Constructor is setting email only, as we are overwriting the password setting work
Here, we’ve solved the stack overflow issue by using _password
private properties. The getter and setter methods access these internal properties instead of directly calling this.password
.
Defining Getters and Setters Using Object.defineProperty
Now in this case Object allows us to define getter and setter as function in itself is an object, so we can call the properties of the object using “.“.
Before JavaScript introduced classes, we could define getters and setters directly using Object.defineProperty
.
Here’s an example:
function User(email, password) {
this._email = email;
this._password = password;
Object.defineProperty(this, "email", {
get: function() {
return this._email.toUpperCase();
},
set: function(value) {
this._email = value;
}
});
Object.defineProperty(this, "password", {
get: function() {
return this._password.toUpperCase();
},
set: function(value) {
this._password = value;
}
});
}
const obj = new User("harsh@gmail.com", "pass123");
console.log(obj.email); // Output: HARSH@GMAIL.COM
console.log(obj.password); // Output: PASS123
Using Object.defineProperty
allows us to explicitly define getter and setter methods, even without using classes. This was common in the earlier days of JavaScript but still works today.
Key points to remember:
When you access obj.email and obj.password, it triggers the getter method, which returns the uppercase version of the email and password.
When you console.log(obj.email), it doesn’t print email directly, but instead, it goes through the getter, converts the email to uppercase, and then prints it.
When you console.log(obj.password), the getter retrieves _password, makes it uppercase, and returns it.
Object-Based Getters and Setters
We can also define getters and setters directly within an object:
const User = {
_email: "harsh@hc.com",
_password: "pass123",
get email() {
return this._email.toUpperCase();
},
set email(value) {
this._email = value;
}
};
const tea = Object.create(User);
console.log(tea.email); // Output: HARSH@HC.COM
You might be thinking this (part one):
we don’t have a variable with the name “email“, so how we are accessing it inside “console.log(tea.email)“.
Reason: When we declare any variable with underscore (_), underscore represents a private property, but when we use get and set, it only sees that if we have any property/variable with that name such as “email“ in this example, and we have that in the form of a function “get(){--}“ and “set(value){- -}“.
The object created “tea“ on the reference of the “User“, therefore “tea“ will also have that “email property/variable“.
You might be thinking this (part 2):
Property we make using getter and setter become function but we are using them as “console.log(tea.email)“ rather than “console.log(tea.email())“.
Reason: Getter and Setter doesn’t let it to be a method, getter and setter are special methods that are put above properties, as we are taking value to the memory using “set“ and bringing the value from the memory using “get“.
So the “get“ and “set“ are special keywords that is working on our property “email“, and it doesn’t care that it is a private property or it is a regular property, it just overwrites the process of taking that value to the memory and bringing it from the memory.
Key Insights:
The
get
andset
keywords in JavaScript allow us to overwrite default behavior and control how properties are accessed and assigned.Getters retrieve the value, while setters allow us to modify it.
When dealing with getters and setters, we must avoid recursion by using different internal variable names (like
_email
or_password
).