Object Literals and Properties in JavaScript

Understanding JavaScript Objects, Properties, and Methods

What Are Objects?

Think of objects as containers that hold related information, like a driver's license that contains your name, address, birth date, and photo. In JavaScript, objects store collections of key-value pairs where each key is like a label and each value is the associated data.

graph TD A[Person Object] --> B[name: 'John Smith'] A --> C[age: 30] A --> D[email: 'john@example.com'] A --> E[address: Object] E --> F[street: '123 Main St'] E --> G[city: 'New York'] E --> H[zipCode: '10001'] style A fill:#f9f,stroke:#333,stroke-width:4px style E fill:#bbf,stroke:#333,stroke-width:2px

Creating Objects

Just like there are many ways to build a house, there are several ways to create objects in JavaScript:

Object Literal Notation (Most Common)

// Empty object
const emptyObject = {};

// Object with properties
const person = {
    firstName: 'John',
    lastName: 'Smith',
    age: 30,
    isStudent: false
};

// Object with nested objects
const employee = {
    id: 1001,
    name: 'Sarah Johnson',
    position: 'Software Engineer',
    contact: {
        email: 'sarah@company.com',
        phone: '555-0123',
        office: 'Building A, Room 302'
    },
    skills: ['JavaScript', 'React', 'Node.js']
};

Object Constructor

// Using Object constructor
const car = new Object();
car.make = 'Toyota';
car.model = 'Camry';
car.year = 2022;

// Using Object.create()
const prototypeObject = {
    greet: function() {
        return `Hello, I'm ${this.name}`;
    }
};

const person = Object.create(prototypeObject);
person.name = 'Alice';
console.log(person.greet()); // "Hello, I'm Alice"

Accessing Object Properties

You can access object properties like reaching into different compartments of a toolbox:

Dot Notation vs Bracket Notation

const user = {
    name: 'John Doe',
    age: 25,
    'favorite color': 'blue',
    123: 'numeric key'
};

// Dot notation (preferred for simple property names)
console.log(user.name);        // 'John Doe'
console.log(user.age);         // 25

// Bracket notation (required for special cases)
console.log(user['favorite color']);  // 'blue'
console.log(user[123]);              // 'numeric key'

// Dynamic property access
const propertyName = 'age';
console.log(user[propertyName]);     // 25

// Accessing nested properties
const company = {
    name: 'Tech Corp',
    address: {
        street: '123 Silicon Valley',
        city: 'San Francisco'
    }
};

console.log(company.address.city);          // 'San Francisco'
console.log(company['address']['street']);  // '123 Silicon Valley'

Adding, Modifying, and Deleting Properties

Objects in JavaScript are mutable - you can change them after creation:

const car = {
    make: 'Honda',
    model: 'Civic'
};

// Adding new properties
car.year = 2023;
car['color'] = 'red';

console.log(car);  
// { make: 'Honda', model: 'Civic', year: 2023, color: 'red' }

// Modifying existing properties
car.color = 'blue';
car['year'] = 2024;

// Deleting properties
delete car.color;
console.log(car);  
// { make: 'Honda', model: 'Civic', year: 2024 }

// Property existence check
console.log('make' in car);      // true
console.log('color' in car);     // false
console.log(car.hasOwnProperty('model')); // true

Object Methods

Methods are functions stored as object properties - like actions that objects can perform:

const calculator = {
    // Properties
    brand: 'Casio',
    model: 'FX-991',
    
    // Methods
    add: function(a, b) {
        return a + b;
    },
    
    // ES6 method syntax
    subtract(a, b) {
        return a - b;
    },
    
    // Method using 'this' keyword
    describe() {
        return `${this.brand} ${this.model} Calculator`;
    }
};

console.log(calculator.add(5, 3));        // 8
console.log(calculator.subtract(10, 4));   // 6
console.log(calculator.describe());        // "Casio FX-991 Calculator"

// Real-world example: User object
const user = {
    firstName: 'Jane',
    lastName: 'Smith',
    birthYear: 1990,
    
    getFullName() {
        return `${this.firstName} ${this.lastName}`;
    },
    
    calculateAge() {
        const currentYear = new Date().getFullYear();
        return currentYear - this.birthYear;
    },
    
    canVote() {
        return this.calculateAge() >= 18;
    }
};

console.log(user.getFullName());  // "Jane Smith"
console.log(user.calculateAge()); // (current year - 1990)
console.log(user.canVote());      // true

Property Descriptors and Object Configuration

Properties have hidden attributes that control their behavior:

const product = {};

// Define a property with specific attributes
Object.defineProperty(product, 'id', {
    value: 'PROD-001',
    writable: false,      // Cannot be changed
    enumerable: true,     // Shows up in for...in loops
    configurable: false   // Cannot be deleted or reconfigured
});

product.id = 'PROD-002';  // This won't work (in strict mode throws error)
console.log(product.id);   // Still 'PROD-001'

// Define multiple properties at once
Object.defineProperties(product, {
    name: {
        value: 'Laptop',
        writable: true,
        enumerable: true
    },
    price: {
        value: 999.99,
        writable: true,
        enumerable: true
    }
});

// Get property descriptor
console.log(Object.getOwnPropertyDescriptor(product, 'id'));
// { value: 'PROD-001', writable: false, enumerable: true, configurable: false }

Iterating Over Objects

There are several ways to loop through object properties:

const student = {
    name: 'Alex Johnson',
    age: 20,
    grade: 'A',
    subjects: ['Math', 'Science', 'History']
};

// for...in loop
console.log('Using for...in:');
for (let key in student) {
    console.log(`${key}: ${student[key]}`);
}

// Object.keys()
console.log('\nUsing Object.keys():');
Object.keys(student).forEach(key => {
    console.log(`${key}: ${student[key]}`);
});

// Object.values()
console.log('\nUsing Object.values():');
Object.values(student).forEach(value => {
    console.log(value);
});

// Object.entries()
console.log('\nUsing Object.entries():');
Object.entries(student).forEach(([key, value]) => {
    console.log(`${key}: ${value}`);
});

// Practical example: Converting object to query string
const params = {
    search: 'javascript',
    page: 2,
    sort: 'relevance'
};

const queryString = Object.entries(params)
    .map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
    .join('&');

console.log(queryString); // "search=javascript&page=2&sort=relevance"

Common Object Patterns

Object as a Dictionary/Map

// Word frequency counter
function countWords(text) {
    const words = text.toLowerCase().split(/\s+/);
    const frequency = {};
    
    words.forEach(word => {
        frequency[word] = (frequency[word] || 0) + 1;
    });
    
    return frequency;
}

const text = "hello world hello javascript world";
console.log(countWords(text));
// { hello: 2, world: 2, javascript: 1 }

// Phone book example
const phoneBook = {
    'John Smith': '555-1234',
    'Jane Doe': '555-5678',
    'Bob Johnson': '555-9012'
};

function findPhoneNumber(name) {
    return phoneBook[name] || 'Not found';
}

Object Factory Pattern

// Factory function to create user objects
function createUser(name, email, role = 'user') {
    return {
        name,
        email,
        role,
        lastLogin: null,
        
        login() {
            this.lastLogin = new Date();
            console.log(`${this.name} logged in at ${this.lastLogin}`);
        },
        
        logout() {
            console.log(`${this.name} logged out`);
        },
        
        changeRole(newRole) {
            this.role = newRole;
            console.log(`${this.name}'s role changed to ${newRole}`);
        }
    };
}

const admin = createUser('Alice Admin', 'alice@example.com', 'admin');
const user = createUser('Bob User', 'bob@example.com');

admin.login();
user.changeRole('moderator');

Real-World Applications

Shopping Cart System

const shoppingCart = {
    items: [],
    
    addItem(product, quantity = 1) {
        const existingItem = this.items.find(item => item.product.id === product.id);
        
        if (existingItem) {
            existingItem.quantity += quantity;
        } else {
            this.items.push({ product, quantity });
        }
    },
    
    removeItem(productId) {
        this.items = this.items.filter(item => item.product.id !== productId);
    },
    
    updateQuantity(productId, quantity) {
        const item = this.items.find(item => item.product.id === productId);
        if (item) {
            item.quantity = quantity;
        }
    },
    
    getTotal() {
        return this.items.reduce((total, item) => {
            return total + (item.product.price * item.quantity);
        }, 0);
    },
    
    checkout() {
        const total = this.getTotal();
        console.log(`Total: $${total.toFixed(2)}`);
        // Process payment...
        this.items = []; // Clear cart
    }
};

// Usage
const product1 = { id: 1, name: 'Laptop', price: 999.99 };
const product2 = { id: 2, name: 'Mouse', price: 29.99 };

shoppingCart.addItem(product1);
shoppingCart.addItem(product2, 2);
console.log(shoppingCart.getTotal()); // 1059.97

Form Validation System

const formValidator = {
    rules: {
        email: {
            required: true,
            pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
            message: 'Please enter a valid email address'
        },
        password: {
            required: true,
            minLength: 8,
            message: 'Password must be at least 8 characters'
        }
    },
    
    validate(formData) {
        const errors = {};
        
        Object.keys(this.rules).forEach(field => {
            const rules = this.rules[field];
            const value = formData[field];
            
            if (rules.required && !value) {
                errors[field] = `${field} is required`;
            } else if (rules.pattern && !rules.pattern.test(value)) {
                errors[field] = rules.message;
            } else if (rules.minLength && value.length < rules.minLength) {
                errors[field] = rules.message;
            }
        });
        
        return {
            isValid: Object.keys(errors).length === 0,
            errors
        };
    }
};

// Usage
const formData = {
    email: 'user@example',
    password: '12345'
};

const validation = formValidator.validate(formData);
console.log(validation);
// { isValid: false, errors: { email: 'Please enter a valid email address', password: 'Password must be at least 8 characters' } }

Best Practices

// Object destructuring
const user = { name: 'John', age: 30, email: 'john@example.com' };
const { name, email } = user;

// Computed property names
const propName = 'dynamicKey';
const obj = {
    [propName]: 'value',
    [`${propName}2`]: 'another value'
};

// Object spread operator
const defaults = { color: 'blue', size: 'medium' };
const userPrefs = { color: 'red' };
const finalSettings = { ...defaults, ...userPrefs };
// { color: 'red', size: 'medium' }

Practice Exercises

  1. Create a "Book" object with properties and methods to track reading progress
  2. Build a simple bank account object with deposit, withdraw, and balance check methods
  3. Create an object that represents a recipe with ingredients and cooking instructions
  4. Implement a student grade tracker that calculates averages and letter grades

Exercise Solutions (Try First!)

Click to see solutions
// 1. Book object
const book = {
    title: 'JavaScript: The Good Parts',
    author: 'Douglas Crockford',
    totalPages: 176,
    currentPage: 0,
    
    readPages(pages) {
        this.currentPage = Math.min(this.currentPage + pages, this.totalPages);
        return this.getProgress();
    },
    
    getProgress() {
        const percentage = (this.currentPage / this.totalPages) * 100;
        return `${percentage.toFixed(1)}% complete`;
    },
    
    isFinished() {
        return this.currentPage === this.totalPages;
    }
};

// 2. Bank account object
const bankAccount = {
    accountNumber: '123456789',
    balance: 1000,
    
    deposit(amount) {
        if (amount > 0) {
            this.balance += amount;
            return `Deposited $${amount}. New balance: $${this.balance}`;
        }
        return 'Invalid deposit amount';
    },
    
    withdraw(amount) {
        if (amount > 0 && amount <= this.balance) {
            this.balance -= amount;
            return `Withdrew $${amount}. New balance: $${this.balance}`;
        }
        return 'Invalid withdrawal amount or insufficient funds';
    },
    
    checkBalance() {
        return `Current balance: $${this.balance}`;
    }
};

// 3. Recipe object
const recipe = {
    name: 'Chocolate Chip Cookies',
    servings: 24,
    ingredients: {
        'flour': '2 cups',
        'sugar': '1 cup',
        'butter': '1 cup',
        'eggs': '2',
        'chocolate chips': '2 cups'
    },
    
    instructions: [
        'Preheat oven to 375°F',
        'Mix dry ingredients',
        'Cream butter and sugar',
        'Add eggs',
        'Combine wet and dry ingredients',
        'Add chocolate chips',
        'Bake for 10-12 minutes'
    ],
    
    scaleRecipe(newServings) {
        const ratio = newServings / this.servings;
        const scaledIngredients = {};
        
        Object.entries(this.ingredients).forEach(([ingredient, amount]) => {
            const [quantity, unit] = amount.split(' ');
            const scaledQuantity = parseFloat(quantity) * ratio;
            scaledIngredients[ingredient] = `${scaledQuantity} ${unit || ''}`.trim();
        });
        
        return scaledIngredients;
    }
};

// 4. Student grade tracker
const studentGrades = {
    name: 'Alice Johnson',
    grades: [],
    
    addGrade(subject, score) {
        this.grades.push({ subject, score });
    },
    
    getAverage() {
        if (this.grades.length === 0) return 0;
        
        const sum = this.grades.reduce((total, grade) => total + grade.score, 0);
        return sum / this.grades.length;
    },
    
    getLetterGrade() {
        const average = this.getAverage();
        if (average >= 90) return 'A';
        if (average >= 80) return 'B';
        if (average >= 70) return 'C';
        if (average >= 60) return 'D';
        return 'F';
    },
    
    getTranscript() {
        return {
            student: this.name,
            grades: this.grades,
            average: this.getAverage().toFixed(2),
            letterGrade: this.getLetterGrade()
        };
    }
};

Summary

Objects are fundamental to JavaScript and provide:

Next up: We'll explore array methods like map, filter, and reduce that transform how we work with collections of data!