JavaScript Object Get/Set

Understanding getters and setters for controlled property access

🎛️ What are Getters and Setters?

Getters and setters are special methods that allow you to define how properties are accessed and modified. They provide a way to control and validate property access while keeping the syntax simple.


let person = {
  get fullName() { return this.first + " " + this.last; },
  set fullName(value) { [this.first, this.last] = value.split(" "); }
};
                                    

Getter and Setter Concepts

📥

Getters

Control how properties are read

get propertyName() {
  return this._value;
}
📤

Setters

Control how properties are written

set propertyName(value) {
  this._value = value;
}

Validation

Validate data before setting

set age(value) {
  if (value < 0) throw new Error("Invalid age");
  this._age = value;
}
🔄

Computed Properties

Calculate values dynamically

get area() {
  return this.width * this.height;
}

🔹 Basic Getters and Setters

Simple example of getters and setters in action:

let person = {
    firstName: "John",
    lastName: "Doe",
    
    // Getter - computed property
    get fullName() {
        return this.firstName + " " + this.lastName;
    },
    
    // Setter - splits full name into parts
    set fullName(value) {
        let parts = value.split(" ");
        this.firstName = parts[0] || "";
        this.lastName = parts[1] || "";
    },
    
    // Getter for formatted display
    get displayName() {
        return this.lastName + ", " + this.firstName;
    }
};

// Using the getter (looks like a property)
console.log("Full name:", person.fullName); // "John Doe"
console.log("Display name:", person.displayName); // "Doe, John"

// Using the setter (looks like assigning to a property)
person.fullName = "Jane Smith";
console.log("First name:", person.firstName); // "Jane"
console.log("Last name:", person.lastName);   // "Smith"
console.log("Full name:", person.fullName);   // "Jane Smith"

Output:

Full name: John Doe
Display name: Doe, John
First name: Jane
Last name: Smith
Full name: Jane Smith

🔹 Getters and Setters with Validation

Use setters to validate data before storing:

let user = {
    _email: "",
    _age: 0,
    
    // Email getter and setter with validation
    get email() {
        return this._email;
    },
    
    set email(value) {
        // Simple email validation
        if (value.includes("@") && value.includes(".")) {
            this._email = value;
        } else {
            console.log("Invalid email format!");
        }
    },
    
    // Age getter and setter with validation
    get age() {
        return this._age;
    },
    
    set age(value) {
        if (typeof value === "number" && value >= 0 && value <= 150) {
            this._age = value;
        } else {
            console.log("Age must be a number between 0 and 150!");
        }
    },
    
    // Computed property based on age
    get category() {
        if (this._age < 13) return "child";
        if (this._age < 20) return "teenager";
        if (this._age < 60) return "adult";
        return "senior";
    }
};

// Test the validation
user.email = "[email protected]";  // Valid
user.age = 25;                    // Valid

console.log("Email:", user.email);     // "[email protected]"
console.log("Age:", user.age);         // 25
console.log("Category:", user.category); // "adult"

// Test invalid values
user.email = "invalid-email";     // Shows error message
user.age = -5;                    // Shows error message
user.age = 200;                   // Shows error message

console.log("Email after invalid:", user.email); // Still "[email protected]"
console.log("Age after invalid:", user.age);     // Still 25

Output:

Email: [email protected]
Age: 25
Category: adult
Invalid email format!
Age must be a number between 0 and 150!
Age must be a number between 0 and 150!
Email after invalid: [email protected]
Age after invalid: 25

🔹 Using Object.defineProperty()

Define getters and setters using Object.defineProperty():

let rectangle = {
    width: 0,
    height: 0
};

// Define getter and setter using Object.defineProperty
Object.defineProperty(rectangle, 'area', {
    get: function() {
        console.log("Calculating area...");
        return this.width * this.height;
    },
    enumerable: true,  // Shows up in for...in loops
    configurable: true // Can be deleted or reconfigured
});

Object.defineProperty(rectangle, 'perimeter', {
    get: function() {
        return 2 * (this.width + this.height);
    },
    enumerable: true
});

// Define a setter for dimensions
Object.defineProperty(rectangle, 'dimensions', {
    set: function(value) {
        if (Array.isArray(value) && value.length === 2) {
            this.width = value[0];
            this.height = value[1];
        } else {
            console.log("Dimensions must be an array of [width, height]");
        }
    },
    enumerable: false // Won't show up in for...in loops
});

// Test the properties
rectangle.width = 5;
rectangle.height = 3;

console.log("Area:", rectangle.area);           // 15 (with log message)
console.log("Perimeter:", rectangle.perimeter); // 16

// Use the setter
rectangle.dimensions = [10, 8];
console.log("New area:", rectangle.area);       // 80
console.log("New perimeter:", rectangle.perimeter); // 36

// Check enumerable properties
console.log("Enumerable properties:", Object.keys(rectangle));

Output:

Calculating area...
Area: 15
Perimeter: 16
Calculating area...
New area: 80
New perimeter: 36
Enumerable properties: ["width", "height", "area", "perimeter"]

🔹 Class-based Getters and Setters

Using getters and setters in ES6 classes:

class Temperature {
    constructor(celsius = 0) {
        this._celsius = celsius;
    }
    
    // Getter for Celsius
    get celsius() {
        return this._celsius;
    }
    
    // Setter for Celsius with validation
    set celsius(value) {
        if (typeof value === "number" && value >= -273.15) {
            this._celsius = value;
        } else {
            throw new Error("Temperature cannot be below absolute zero (-273.15°C)");
        }
    }
    
    // Getter for Fahrenheit (computed property)
    get fahrenheit() {
        return (this._celsius * 9/5) + 32;
    }
    
    // Setter for Fahrenheit
    set fahrenheit(value) {
        this.celsius = (value - 32) * 5/9;
    }
    
    // Getter for Kelvin
    get kelvin() {
        return this._celsius + 273.15;
    }
    
    // Setter for Kelvin
    set kelvin(value) {
        this.celsius = value - 273.15;
    }
    
    // Method to get description
    get description() {
        if (this._celsius < 0) return "Freezing";
        if (this._celsius < 10) return "Cold";
        if (this._celsius < 25) return "Cool";
        if (this._celsius < 35) return "Warm";
        return "Hot";
    }
}

// Create temperature object
let temp = new Temperature(25);

console.log("Celsius:", temp.celsius);       // 25
console.log("Fahrenheit:", temp.fahrenheit); // 77
console.log("Kelvin:", temp.kelvin);         // 298.15
console.log("Description:", temp.description); // "Warm"

// Change temperature using different scales
temp.fahrenheit = 100;
console.log("After setting to 100°F:");
console.log("Celsius:", temp.celsius);       // ~37.78
console.log("Description:", temp.description); // "Hot"

temp.kelvin = 273.15;
console.log("After setting to 273.15K:");
console.log("Celsius:", temp.celsius);       // 0
console.log("Fahrenheit:", temp.fahrenheit); // 32

Output:

Celsius: 25
Fahrenheit: 77
Kelvin: 298.15
Description: Warm
After setting to 100°F:
Celsius: 37.77777777777778
Description: Hot
After setting to 273.15K:
Celsius: 0
Fahrenheit: 32

🔹 Practical Examples

Real-world use cases for getters and setters:

// Example 1: Shopping Cart with automatic calculations
let shoppingCart = {
    _items: [],
    
    get items() {
        return this._items;
    },
    
    set items(newItems) {
        this._items = Array.isArray(newItems) ? newItems : [];
    },
    
    get totalPrice() {
        return this._items.reduce((sum, item) => sum + (item.price * item.quantity), 0);
    },
    
    get itemCount() {
        return this._items.reduce((sum, item) => sum + item.quantity, 0);
    },
    
    addItem(name, price, quantity = 1) {
        this._items.push({name, price, quantity});
    }
};

// Example 2: User profile with automatic formatting
let userProfile = {
    _firstName: "",
    _lastName: "",
    _birthYear: null,
    
    get firstName() { return this._firstName; },
    set firstName(value) { 
        this._firstName = typeof value === "string" ? value.trim() : "";
    },
    
    get lastName() { return this._lastName; },
    set lastName(value) { 
        this._lastName = typeof value === "string" ? value.trim() : "";
    },
    
    get fullName() {
        return `${this._firstName} ${this._lastName}`.trim();
    },
    
    get age() {
        return this._birthYear ? new Date().getFullYear() - this._birthYear : null;
    },
    
    set birthYear(year) {
        let currentYear = new Date().getFullYear();
        if (year > 1900 && year <= currentYear) {
            this._birthYear = year;
        }
    }
};

// Test shopping cart
shoppingCart.addItem("Laptop", 999, 1);
shoppingCart.addItem("Mouse", 25, 2);

console.log("Cart total:", shoppingCart.totalPrice); // 1049
console.log("Item count:", shoppingCart.itemCount);  // 3

// Test user profile
userProfile.firstName = "  john  ";
userProfile.lastName = "  doe  ";
userProfile.birthYear = 1990;

console.log("Full name:", userProfile.fullName); // "john doe"
console.log("Age:", userProfile.age);            // Current year - 1990

Output:

Cart total: 1049
Item count: 3
Full name: john doe
Age: 34

🧠 Test Your Knowledge

What's the main benefit of using getters and setters?