10. Objects and Prototypes

Object Creation

Object Literal

const person = {
    name: "John",
    age: 30,
    greet: function() {
        return `Hello, I'm ${this.name}`;
    }
};

console.log(person.name);     // "John"
console.log(person.greet());  // "Hello, I'm John"

Constructor Function

function Person(name, age) {
    this.name = name;
    this.age = age;
    this.greet = function() {
        return `Hello, I'm ${this.name}`;
    };
}

const john = new Person("John", 30);
console.log(john.greet()); // "Hello, I'm John"

Object.create()

const personPrototype = {
    greet: function() {
        return `Hello, I'm ${this.name}`;
    }
};

const john = Object.create(personPrototype);
john.name = "John";
john.age = 30;

console.log(john.greet()); // "Hello, I'm John"

Properties and Methods

Adding Properties

const car = {};

// Dot notation
car.make = "Toyota";
car.model = "Camry";

// Bracket notation
car["year"] = 2020;
car["color"] = "blue";

console.log(car); // { make: "Toyota", model: "Camry", year: 2020, color: "blue" }

Accessing Properties

const person = {
    name: "John",
    age: 30,
    "first-name": "John" // Property with special characters
};

// Dot notation
console.log(person.name);  // "John"

// Bracket notation
console.log(person["age"]); // 30
console.log(person["first-name"]); // "John"

// Dynamic property access
const property = "name";
console.log(person[property]); // "John"

Property Descriptors

const person = {};

Object.defineProperty(person, "name", {
    value: "John",
    writable: true,     // Can change value
    enumerable: true,   // Shows in for...in loops
    configurable: true  // Can delete or reconfigure
});

Object.defineProperty(person, "age", {
    get: function() {
        return this._age || 0;
    },
    set: function(value) {
        this._age = value > 0 ? value : 0;
    },
    enumerable: true,
    configurable: true
});

person.age = 30;
console.log(person.age); // 30
person.age = -5;
console.log(person.age); // 0

Prototypes

Understanding Prototypes

function Person(name) {
    this.name = name;
}

Person.prototype.greet = function() {
    return `Hello, I'm ${this.name}`;
};

const john = new Person("John");
console.log(john.greet()); // "Hello, I'm John"

// Check prototype
console.log(john.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true

Prototype Chain

const grandparent = { a: 1 };
const parent = Object.create(grandparent);
parent.b = 2;
const child = Object.create(parent);
child.c = 3;

console.log(child.a); // 1 (inherited from grandparent)
console.log(child.b); // 2 (inherited from parent)
console.log(child.c); // 3 (own property)

instanceof Operator

function Animal(name) {
    this.name = name;
}

function Dog(name, breed) {
    Animal.call(this, name);
    this.breed = breed;
}

Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

const dog = new Dog("Buddy", "Golden Retriever");

console.log(dog instanceof Dog);     // true
console.log(dog instanceof Animal);  // true
console.log(dog instanceof Object);  // true

Object Methods

Object.keys(), Object.values(), Object.entries()

const person = {
    name: "John",
    age: 30,
    city: "New York"
};

console.log(Object.keys(person));    // ["name", "age", "city"]
console.log(Object.values(person));  // ["John", 30, "New York"]
console.log(Object.entries(person)); // [["name", "John"], ["age", 30], ["city", "New York"]]

Object.assign()

const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };

Object.assign(target, source1, source2);
console.log(target); // { a: 1, b: 2, c: 3 }

// Shallow copy
const original = { a: 1, b: { c: 2 } };
const copy = Object.assign({}, original);
copy.b.c = 3;
console.log(original.b.c); // 3 (modified!)

Object.freeze(), Object.seal()

const obj = { a: 1, b: 2 };

// Freeze: prevents adding, deleting, modifying properties
Object.freeze(obj);
obj.a = 3;        // No effect in strict mode
obj.c = 4;        // No effect
delete obj.b;     // No effect

// Seal: prevents adding, deleting properties but allows modification
const obj2 = { x: 1, y: 2 };
Object.seal(obj2);
obj2.x = 3;       // OK
obj2.z = 4;       // No effect
delete obj2.y;    // No effect

JSON

JSON.stringify()

const person = {
    name: "John",
    age: 30,
    hobbies: ["reading", "coding"]
};

const jsonString = JSON.stringify(person);
console.log(jsonString); // '{"name":"John","age":30,"hobbies":["reading","coding"]}'

JSON.parse()

const jsonString = '{"name":"John","age":30}';
const person = JSON.parse(jsonString);
console.log(person.name); // "John"

Next Steps

Objects and prototypes form the foundation of JavaScript's object-oriented programming. Next, let's explore classes, the modern way to create objects in ECMAScript.