What are Getters and Setters in JavaScript and How to Implement Them

JavaScript getters and setters are special methods that allow you to define how object properties are accessed and modified. Think of them as “smart properties” that can execute code when you read from or write to them, unlike regular object properties that simply store values.

Let’s start with a fundamental understanding of these powerful features and gradually build up to more complex implementations.

What Are Getters and Setters?

Getters and setters are special methods that allow you to define how object properties are accessed (gotten) and modified (set). Think of them as “interceptors” that let you add logic whenever someone tries to read or change a property’s value.

Understanding Getters

A getter is a method that gets the value of a specific property. When you access the property, the getter method is called automatically, allowing you to compute values dynamically or add validation before returning the value.

const person = {
  firstName: 'John',
  lastName: 'Doe',
  get fullName() {
    return `${this.firstName} ${this.lastName}`;
  }
};

console.log(person.fullName); // Output: "John Doe"

In this example, fullName is a getter that combines firstName and lastName. When we access person.fullName, the getter method runs automatically and returns the computed value.

Understanding Setters

A setter is a method that sets the value of a specific property. When you assign a value to the property, the setter method is called automatically, letting you validate or transform the value before it’s stored.

const thermostat = {
  _temperature: 20,
  set temperature(value) {
    if (value < 0 || value > 40) {
      console.log('Temperature must be between 0 and 40 degrees');
      return;
    }
    this._temperature = value;
  }
};

thermostat.temperature = 25; // Sets temperature to 25
thermostat.temperature = 50; // Logs error message

Here, the setter validates the temperature value before allowing it to be changed. The underscore prefix _temperature is a common convention indicating this is a private property.

Why Use Getters and Setters?

Data Validation

One of the primary benefits of setters is data validation. You can ensure that properties only accept valid values:

const bankAccount = {
  _balance: 0,
  set balance(value) {
    if (typeof value !== 'number') {
      throw new Error('Balance must be a number');
    }
    if (value < 0) {
      throw new Error('Balance cannot be negative');
    }
    this._balance = value;
  }
};

This setter ensures the balance is always a non-negative number, maintaining data integrity.

Computed Properties

Getters excel at creating computed properties that are calculated based on other values:

const circle = {
  radius: 5,
  get area() {
    return Math.PI * this.radius ** 2;
  },
  get circumference() {
    return 2 * Math.PI * this.radius;
  }
};

These getters automatically calculate the area and circumference whenever they’re accessed, ensuring values are always up to date.

Different Ways to Define Getters and Setters

Object Literal Syntax

The simplest way to define getters and setters is within an object literal:

const product = {
  name: 'Laptop',
  price: 999,
  get priceWithTax() {
    return this.price * 1.2;
  },
  set price(value) {
    if (value <= 0) throw new Error('Price must be positive');
    this._price = value;
  }
};

Using Object.defineProperty()

You can also add getters and setters to existing objects using Object.defineProperty():

const user = {
  firstName: 'Jane',
  lastName: 'Smith'
};

Object.defineProperty(user, 'fullName', {
  get: function() {
    return `${this.firstName} ${this.lastName}`;
  },
  set: function(value) {
    [this.firstName, this.lastName] = value.split(' ');
  }
});

This approach is useful when you need to add accessors to objects dynamically.

Best Practices for Using Getters and Setters

1. Keep Getters Pure

Getters should not modify the object’s state. They should simply return a value based on existing properties:

const good = {
  items: [],
  get count() {
    return this.items.length; // Good: just returns a value
  }
};

const bad = {
  items: [],
  get count() {
    this.items.push('new'); // Bad: modifies state
    return this.items.length;
  }
};

2. Use Clear Naming Conventions

Follow consistent naming conventions for private properties and their accessors:

const user = {
  _email: '', // Private property
  get email() {
    return this._email;
  },
  set email(value) {
    if (!value.includes('@')) throw new Error('Invalid email');
    this._email = value;
  }
};

3. Implement Error Handling

Always include proper error handling in setters to maintain data integrity:

const account = {
  _balance: 0,
  set balance(value) {
    try {
      if (typeof value !== 'number') throw new TypeError('Balance must be a number');
      if (value < 0) throw new RangeError('Balance cannot be negative');
      this._balance = value;
    } catch (error) {
      console.error(`Error setting balance: ${error.message}`);
      throw error;
    }
  }
};

Common Use Cases and Examples

Form Validation

Getters and setters are perfect for form validation:

const form = {
  _password: '',
  set password(value) {
    if (value.length < 8) {
      throw new Error('Password must be at least 8 characters');
    }
    if (!/[A-Z]/.test(value)) {
      throw new Error('Password must contain an uppercase letter');
    }
    this._password = value;
  }
};

Unit Conversion

Use getters to handle unit conversions automatically:

const temperature = {
  _celsius: 0,
  get celsius() {
    return this._celsius;
  },
  get fahrenheit() {
    return (this._celsius * 9/5) + 32;
  },
  set celsius(value) {
    this._celsius = value;
  },
  set fahrenheit(value) {
    this._celsius = (value - 32) * 5/9;
  }
};

Advanced Topics

Using Getters and Setters with Classes

Getters and setters work seamlessly with JavaScript classes:

class Person {
  constructor(firstName, lastName) {
    this._firstName = firstName;
    this._lastName = lastName;
  }

  get fullName() {
    return `${this._firstName} ${this._lastName}`;
  }

  set fullName(value) {
    [this._firstName, this._lastName] = value.split(' ');
  }
}

const person = new Person('John', 'Doe');
console.log(person.fullName); // "John Doe"
person.fullName = 'Jane Smith';
console.log(person.fullName); // "Jane Smith"

Summary and Key Takeaways

JavaScript getters and setters are powerful features that give you control over how object properties are accessed and modified. Here are the key points to remember:

  1. Getters retrieve values and can compute them dynamically
  2. Setters modify values and can include validation
  3. They help maintain data integrity and encapsulation
  4. They’re useful for computed properties and data validation
  5. Best practices include keeping getters pure and implementing proper error handling

Tips for Mastering Getters and Setters

  • Start with simple examples and gradually move to more complex use cases
  • Practice implementing validation logic in setters
  • Use computed properties with getters when values depend on other properties
  • Remember to handle edge cases and errors
  • Follow consistent naming conventions for private properties

With these concepts and examples in hand, you’re well-equipped to start using getters and setters in your JavaScript projects. Remember that practice is key – try implementing these patterns in your own code to better understand their power and flexibility.

Happy coding!

Previous Article

What are JavaScript Constructors and How to Use Them to Create Objects For Beginners

Next Article

What are JavaScript Prototypes, Prototype Chain and does they work

Write a Comment

Leave a Comment

Your email address will not be published. Required fields are marked *

Subscribe to our Newsletter

Subscribe to our email newsletter to get the latest posts delivered right to your email.
Pure inspiration, zero spam ✨