JavaScript Getters and Setters Explained

Getters and setters in JavaScript are special methods that define how object properties are accessed and modified. Instead of holding raw data like standard properties, they act as accessor functions bound to a property name. This article explains how getters and setters work in both object literals and ES6 classes, detailing their syntax, practical use cases, and how they enhance data encapsulation and validation.

What Are Getters and Setters?

In JavaScript, object properties are categorized into data properties and accessor properties. Accessor properties do not store values directly; instead, they use:

Getters and Setters in Object Literals

In an object literal, getters and setters are defined using the get and set keywords directly before a method name.

const user = {
  firstName: 'Jane',
  lastName: 'Doe',

  // Getter: computed property
  get fullName() {
    return `${this.firstName} ${this.lastName}`;
  },

  // Setter: updates underlying properties
  set fullName(name) {
    const parts = name.trim().split(' ');
    if (parts.length < 2) {
      throw new Error('Please provide both first and last names.');
    }
    this.firstName = parts[0];
    this.lastName = parts[1];
  }
};

// Accessing the getter (no parentheses required)
console.log(user.fullName); // Output: "Jane Doe"

// Invoking the setter
user.fullName = 'John Smith';
console.log(user.firstName); // Output: "John"
console.log(user.lastName);  // Output: "Smith"

Getters and Setters in ES6 Classes

In classes, getters and setters provide a clean interface for managing class fields, especially when enforcing encapsulation with private fields (prefixed with #) or internal naming conventions (prefixed with _).

class BankAccount {
  #balance = 0; // Private field

  constructor(initialBalance) {
    this.balance = initialBalance; // Calls the setter
  }

  // Getter
  get balance() {
    return this.#balance;
  }

  // Setter with validation
  set balance(amount) {
    if (typeof amount !== 'number' || amount < 0) {
      throw new Error('Balance must be a positive number.');
    }
    this.#balance = amount;
  }

  // Computed getter
  get formattedBalance() {
    return `$${this.#balance.toFixed(2)}`;
  }
}

const account = new BankAccount(100);

console.log(account.balance);          // Output: 100
console.log(account.formattedBalance); // Output: "$100.00"

account.balance = 250;
console.log(account.formattedBalance); // Output: "$250.00"

// account.balance = -50; // Throws Error: Balance must be a positive number.

Key Reasons to Use Getters and Setters

  1. Encapsulation and Data Validation: Setters let you enforce business rules and validate incoming values before mutating internal state.
  2. Computed Properties: Getters derive dynamic values on the fly without duplicating stored state.
  3. Backward Compatibility: You can turn a regular data property into an accessor property without altering the public API or breaking external code that reads or writes to the property directly.
  4. Cleaner Syntax: Properties are accessed and assigned using standard dot notation (obj.prop = value), avoiding the need for explicit method calls like obj.getProp() and obj.setProp(value).