Chapter 7: The Object Kingdom Chronicles
Objects are the heart of JavaScript Land. Everything else either is an object or can become one.
Object Creation and Access
// Object literal syntax let wizard = { name: "Gandalf", age: 2019, staff: "wooden", spells: ["fireball", "lightning", "heal"], // Methods castSpell: function(spellName) { return `${this.name} casts ${spellName}!`; }, // ES6 method shorthand meditate() { return `${this.name} is meditating...`; } }; // Property access console.log(wizard.name); // Dot notation console.log(wizard["age"]); // Bracket notation console.log(wizard.spells[0]); // Accessing array property // Dynamic property access let prop = "staff"; console.log(wizard[prop]); // "wooden" // Computed property names let skill = "magic"; let level = "expert"; let mage = { name: "Merlin", [skill]: level, // Computed property: magic: "expert" [`${skill}Level`]: 95 // magicLevel: 95 };
Object Destructuring - The Property Extractor
let hero = { name: "Wonder Woman", realName: "Diana Prince", powers: ["super strength", "flight", "lasso of truth"], team: "Justice League" }; // Basic destructuring let { name, powers } = hero; console.log(name); // "Wonder Woman" console.log(powers); // ["super strength", "flight", "lasso of truth"] // Renaming variables let { realName: secretIdentity } = hero; console.log(secretIdentity); // "Diana Prince" // Default values let { weakness = "none", team } = hero; console.log(weakness); // "none" console.log(team); // "Justice League" // Nested destructuring let villain = { name: "Joker", base: { location: "Arkham Asylum", security: "maximum" } }; let { base: { location, security } } = villain; console.log(location); // "Arkham Asylum" console.log(security); // "maximum" // Rest in object destructuring let { name: heroName, ...otherProps } = hero; console.log(heroName); // "Wonder Woman" console.log(otherProps); // { realName: "Diana Prince", powers: [...], team: "Justice League" }
Object Methods and this Context
let calculator = { result: 0, add(num) { this.result += num; return this; // Return this for method chaining }, multiply(num) { this.result *= num; return this; }, reset() { this.result = 0; return this; }, getValue() { return this.result; } }; // Method chaining let finalResult = calculator.add(5).multiply(3).add(2).getValue(); console.log(finalResult); // 17 // The 'this' binding can be tricky let obj = { name: "Alice", greet() { console.log(`Hello, I'm ${this.name}`); } }; obj.greet(); // "Hello, I'm Alice" // Lost 'this' context let greetFunction = obj.greet; greetFunction(); // "Hello, I'm undefined" (in strict mode) or global name // Solutions: bind, call, apply let boundGreet = obj.greet.bind(obj); boundGreet(); // "Hello, I'm Alice" obj.greet.call(obj); // "Hello, I'm Alice" obj.greet.apply(obj); // "Hello, I'm Alice"
Object Manipulation Techniques
// Object.keys, Object.values, Object.entries let book = { title: "JavaScript: The Good Parts", author: "Douglas Crockford", year: 2008, pages: 172 }; console.log(Object.keys(book)); // ["title", "author", "year", "pages"] console.log(Object.values(book)); // ["JavaScript: The Good Parts", "Douglas Crockford", 2008, 172] console.log(Object.entries(book)); // [["title", "JavaScript: The Good Parts"], ...] // Object.assign - shallow copy and merge let defaults = { theme: "dark", language: "en" }; let userPrefs = { theme: "light", fontSize: 14 }; let settings = Object.assign({}, defaults, userPrefs); console.log(settings); // { theme: "light", language: "en", fontSize: 14 } // Spread operator with objects (ES6+) let newSettings = { ...defaults, ...userPrefs, autoSave: true }; console.log(newSettings); // { theme: "light", language: "en", fontSize: 14, autoSave: true } // Property descriptors Object.defineProperty(book, 'isbn', { value: '978-0596517748', writable: false, // Can't be changed enumerable: true, // Shows up in for...in loops configurable: false // Can't be deleted or redefined }); // Check if property exists console.log('title' in book); // true console.log(book.hasOwnProperty('title')); // true console.log('toString' in book); // true (inherited) console.log(book.hasOwnProperty('toString')); // false (inherited)
Advanced Object Patterns
Factory Functions and Object Creation
// Factory function pattern function createPerson(name, age) { return { name, age, greet() { return `Hi, I'm ${this.name} and I'm ${this.age} years old`; }, haveBirthday() { this.age++; return `Happy birthday! Now ${this.age} years old`; } }; } let john = createPerson("John", 25); console.log(john.greet()); // "Hi, I'm John and I'm 25 years old" // Constructor function pattern function Person(name, age) { this.name = name; this.age = age; } Person.prototype.greet = function() { return `Hi, I'm ${this.name}`; }; let jane = new Person("Jane", 30); console.log(jane.greet()); // "Hi, I'm Jane"
Object.create and Prototypal Inheritance
// Create object with specific prototype let animal = { type: "unknown", makeSound() { return "Some generic sound"; } }; let dog = Object.create(animal); dog.type = "canine"; dog.makeSound = function() { return "Woof!"; }; console.log(dog.makeSound()); // "Woof!" console.log(dog.__proto__ === animal); // true // Null prototype object let pureObject = Object.create(null); console.log(pureObject.toString); // undefined (no inherited methods)
Getters and Setters
let user = { firstName: "John", lastName: "Doe", // Getter get fullName() { return `${this.firstName} ${this.lastName}`; }, // Setter set fullName(value) { [this.firstName, this.lastName] = value.split(' '); }, // Computed property with validation _age: 0, get age() { return this._age; }, set age(value) { if (value < 0 || value > 150) { throw new Error("Invalid age"); } this._age = value; } }; console.log(user.fullName); // "John Doe" user.fullName = "Jane Smith"; console.log(user.firstName); // "Jane" console.log(user.lastName); // "Smith"
Object Freezing and Sealing
// Object.freeze - no modifications allowed let frozenObj = Object.freeze({ name: "Frozen", nested: { value: 42 } }); frozenObj.name = "Changed"; // Silently fails (error in strict mode) frozenObj.newProp = "New"; // Silently fails delete frozenObj.name; // Silently fails frozenObj.nested.value = 100; // Works! (shallow freeze) // Object.seal - no adding/deleting, but can modify let sealedObj = Object.seal({ name: "Sealed", value: 42 }); sealedObj.name = "Modified"; // Works sealedObj.newProp = "New"; // Silently fails delete sealedObj.value; // Silently fails // Object.preventExtensions - no new properties let obj = { existing: true }; Object.preventExtensions(obj); obj.existing = false; // Works obj.newProp = "new"; // Silently fails
Common Object Pitfalls
// Object comparison let obj1 = { a: 1 }; let obj2 = { a: 1 }; console.log(obj1 === obj2); // false (different references) console.log(obj1 == obj2); // false // Shallow vs Deep copy let original = { name: "Original", nested: { value: 42 } }; let shallowCopy = { ...original }; shallowCopy.nested.value = 100; console.log(original.nested.value); // 100 (nested object is shared!) // Deep copy with JSON (limited) let deepCopy = JSON.parse(JSON.stringify(original)); deepCopy.nested.value = 200; console.log(original.nested.value); // 100 (truly independent) // But JSON method has limitations let complexObj = { date: new Date(), func: () => "hello", undef: undefined, symbol: Symbol("sym") }; let jsonCopy = JSON.parse(JSON.stringify(complexObj)); console.log(jsonCopy); // { date: "2023-...", func/undef/symbol missing }
Performance Considerations
// Use object literals when possible let fast = { a: 1, b: 2 }; // Optimized by engine // Avoid delete operator in hot code let obj = { a: 1, b: 2, c: 3 }; // delete obj.b; // Slow - changes object shape obj.b = undefined; // Faster alternative // Hidden classes optimization // Keep object shape consistent function Point(x, y) { this.x = x; this.y = y; } // All Points have same shape - optimized let p1 = new Point(1, 2); let p2 = new Point(3, 4); // Avoid changing shape after creation // p1.z = 5; // De-optimizes the hidden class
Next Chapter: Chapter 8: The Scope and Closure Mysteries
Previous Chapter: Chapter 6: The Array Adventures
Table of Contents: JavaScript Guide
