Advanced JavaScript (ES6+)

Object Methods

13 min Lesson 29 of 40

Object Methods

JavaScript provides a rich set of built-in methods for working with objects. These methods allow you to iterate over object properties, merge objects, control property behavior, and much more. Let's explore the most useful object methods in modern JavaScript.

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

These methods provide different ways to iterate over object properties:

const user = { name: "John Doe", age: 30, email: "john@example.com", role: "developer" }; // Object.keys() - returns array of property names const keys = Object.keys(user); console.log(keys); // ["name", "age", "email", "role"] // Object.values() - returns array of property values const values = Object.values(user); console.log(values); // ["John Doe", 30, "john@example.com", "developer"] // Object.entries() - returns array of [key, value] pairs const entries = Object.entries(user); console.log(entries); // [["name", "John Doe"], ["age", 30], ["email", "john@example.com"], ["role", "developer"]] // Iterate over entries Object.entries(user).forEach(([key, value]) => { console.log(`${key}: ${value}`); }); // Output: // name: John Doe // age: 30 // email: john@example.com // role: developer
Use Cases: These methods are perfect for converting objects to arrays for mapping, filtering, or reducing operations.

Object.assign() for Merging Objects

Object.assign() copies properties from source objects to a target object:

// Basic usage const target = { a: 1, b: 2 }; const source1 = { b: 3, c: 4 }; const source2 = { c: 5, d: 6 }; const result = Object.assign(target, source1, source2); console.log(result); // { a: 1, b: 3, c: 5, d: 6 } console.log(target); // { a: 1, b: 3, c: 5, d: 6 } (target is modified!) // Creating a new object (common pattern) const obj1 = { x: 1, y: 2 }; const obj2 = { y: 3, z: 4 }; const merged = Object.assign({}, obj1, obj2); console.log(merged); // { x: 1, y: 3, z: 4 } console.log(obj1); // { x: 1, y: 2 } (original unchanged) // Cloning an object (shallow copy) const original = { name: "John", age: 30 }; const clone = Object.assign({}, original); console.log(clone); // { name: "John", age: 30 } // Modern alternative: spread operator const modernClone = { ...original }; const modernMerged = { ...obj1, ...obj2 };
Important: Object.assign() performs a shallow copy. Nested objects are copied by reference, not value.

Object.freeze() and Object.seal()

These methods control whether an object can be modified:

// Object.freeze() - makes object immutable const frozenUser = { name: "John", age: 30 }; Object.freeze(frozenUser); frozenUser.age = 31; // Silently fails (throws error in strict mode) frozenUser.email = "john@example.com"; // Cannot add new properties delete frozenUser.name; // Cannot delete properties console.log(frozenUser); // { name: "John", age: 30 } // Check if frozen console.log(Object.isFrozen(frozenUser)); // true // Object.seal() - prevents adding/removing properties but allows modification const sealedUser = { name: "Jane", age: 25 }; Object.seal(sealedUser); sealedUser.age = 26; // Works! Can modify existing properties sealedUser.email = "jane@example.com"; // Cannot add new properties delete sealedUser.name; // Cannot delete properties console.log(sealedUser); // { name: "Jane", age: 26 } console.log(Object.isSealed(sealedUser)); // true
Differences: Object.freeze(): - Cannot add properties - Cannot remove properties - Cannot modify properties - Use for true immutability Object.seal(): - Cannot add properties - Cannot remove properties - CAN modify existing properties - Use when you want fixed structure but mutable values

Object.defineProperty() and Property Descriptors

Define or modify object properties with precise control:

const person = {}; // Define a property with descriptors Object.defineProperty(person, "name", { value: "John", writable: true, // Can be changed enumerable: true, // Shows up in loops configurable: true // Can be deleted or reconfigured }); // Define a read-only property Object.defineProperty(person, "id", { value: 12345, writable: false, // Cannot be changed enumerable: true, configurable: false // Cannot be deleted }); // Define a hidden property Object.defineProperty(person, "password", { value: "secret123", writable: true, enumerable: false, // Won't show in Object.keys() or for...in configurable: true }); console.log(person.name); // "John" console.log(person.id); // 12345 person.id = 99999; // Fails silently (throws error in strict mode) console.log(person.id); // 12345 (unchanged) console.log(Object.keys(person)); // ["name", "id"] (password is hidden) // Get property descriptor const descriptor = Object.getOwnPropertyDescriptor(person, "name"); console.log(descriptor); // { value: "John", writable: true, enumerable: true, configurable: true }

Getters and Setters with Object.defineProperty()

Create computed properties with getters and setters:

const user = { firstName: "John", lastName: "Doe" }; // Define a getter Object.defineProperty(user, "fullName", { get() { return `${this.firstName} ${this.lastName}`; }, set(value) { const parts = value.split(" "); this.firstName = parts[0]; this.lastName = parts[1]; }, enumerable: true, configurable: true }); console.log(user.fullName); // "John Doe" user.fullName = "Jane Smith"; console.log(user.firstName); // "Jane" console.log(user.lastName); // "Smith" console.log(user.fullName); // "Jane Smith" // Getters and setters in object literals (modern syntax) const rectangle = { width: 10, height: 5, get area() { return this.width * this.height; }, set dimensions(value) { [this.width, this.height] = value.split("x").map(Number); } }; console.log(rectangle.area); // 50 rectangle.dimensions = "20x15"; console.log(rectangle.area); // 300
Best Practice: Use getters for computed properties and setters for validation or side effects when properties change.

Object.create() for Prototypal Inheritance

Create objects with a specific prototype:

// Create a prototype object const animalPrototype = { eat() { return `${this.name} is eating`; }, sleep() { return `${this.name} is sleeping`; } }; // Create objects with this prototype const dog = Object.create(animalPrototype); dog.name = "Buddy"; dog.bark = function() { return `${this.name} says Woof!`; }; const cat = Object.create(animalPrototype); cat.name = "Whiskers"; cat.meow = function() { return `${this.name} says Meow!`; }; console.log(dog.eat()); // "Buddy is eating" console.log(dog.bark()); // "Buddy says Woof!" console.log(cat.sleep()); // "Whiskers is sleeping" console.log(cat.meow()); // "Whiskers says Meow!" // Create with properties and descriptors const person = Object.create(animalPrototype, { name: { value: "John", writable: true, enumerable: true }, age: { value: 30, writable: true, enumerable: true } }); console.log(person.name); // "John" console.log(person.eat()); // "John is eating"

Object.hasOwn() and hasOwnProperty()

Check if an object has a property (not inherited):

const parent = { parentProp: "I'm from parent" }; const child = Object.create(parent); child.childProp = "I'm from child"; // hasOwnProperty (older method) console.log(child.hasOwnProperty("childProp")); // true console.log(child.hasOwnProperty("parentProp")); // false console.log(child.hasOwnProperty("toString")); // false // Object.hasOwn() (modern, safer) console.log(Object.hasOwn(child, "childProp")); // true console.log(Object.hasOwn(child, "parentProp")); // false // "in" operator checks entire prototype chain console.log("childProp" in child); // true console.log("parentProp" in child); // true console.log("toString" in child); // true
Recommendation: Prefer Object.hasOwn() over hasOwnProperty() as it's safer and works with objects created via Object.create(null).

Object.is() for Equality Comparison

Object.is() determines if two values are the same:

// Object.is() vs === operator console.log(Object.is(25, 25)); // true console.log(Object.is("hello", "hello")); // true console.log(Object.is(true, true)); // true // Special cases where Object.is() differs from === // NaN comparison console.log(NaN === NaN); // false console.log(Object.is(NaN, NaN)); // true // +0 and -0 console.log(+0 === -0); // true console.log(Object.is(+0, -0)); // false // Object comparison (both check reference) const obj1 = { a: 1 }; const obj2 = { a: 1 }; const obj3 = obj1; console.log(obj1 === obj2); // false (different references) console.log(Object.is(obj1, obj2)); // false console.log(obj1 === obj3); // true (same reference) console.log(Object.is(obj1, obj3)); // true

Object.fromEntries() and Converting Data Structures

Convert arrays of key-value pairs back to objects:

// Convert array of entries to object const entries = [ ["name", "John"], ["age", 30], ["role", "developer"] ]; const user = Object.fromEntries(entries); console.log(user); // { name: "John", age: 30, role: "developer" } // Convert Map to object const map = new Map([ ["firstName", "John"], ["lastName", "Doe"], ["age", 30] ]); const objFromMap = Object.fromEntries(map); console.log(objFromMap); // { firstName: "John", lastName: "Doe", age: 30 } // Practical use: Transform object values const prices = { apple: 1.5, banana: 0.8, orange: 2.0 }; // Apply 20% discount const discountedPrices = Object.fromEntries( Object.entries(prices).map(([key, value]) => [key, value * 0.8]) ); console.log(discountedPrices); // { apple: 1.2, banana: 0.64, orange: 1.6 }

Object.getOwnPropertyNames() and Object.getOwnPropertySymbols()

Get all properties, including non-enumerable ones:

const obj = { visibleProp: "I'm visible" }; Object.defineProperty(obj, "hiddenProp", { value: "I'm hidden", enumerable: false }); const symbol = Symbol("symbolProp"); obj[symbol] = "I'm a symbol property"; // Object.keys() only returns enumerable string properties console.log(Object.keys(obj)); // ["visibleProp"] // Object.getOwnPropertyNames() returns all string properties console.log(Object.getOwnPropertyNames(obj)); // ["visibleProp", "hiddenProp"] // Object.getOwnPropertySymbols() returns symbol properties console.log(Object.getOwnPropertySymbols(obj)); // [Symbol(symbolProp)] // Get ALL properties (strings and symbols) const allProps = [ ...Object.getOwnPropertyNames(obj), ...Object.getOwnPropertySymbols(obj) ]; console.log(allProps); // ["visibleProp", "hiddenProp", Symbol(symbolProp)]

Practice Exercise:

Challenge: Create a utility object with the following methods:

  • deepClone(obj) - Creates a deep copy of an object
  • pick(obj, keys) - Returns a new object with only specified keys
  • omit(obj, keys) - Returns a new object without specified keys
  • isEmpty(obj) - Checks if object has no properties

Solution:

const ObjectUtils = { // Deep clone using JSON (simple but has limitations) deepClone(obj) { if (obj === null || typeof obj !== "object") { return obj; } if (obj instanceof Date) { return new Date(obj); } if (obj instanceof Array) { return obj.map(item => this.deepClone(item)); } const cloned = {}; for (const key in obj) { if (Object.hasOwn(obj, key)) { cloned[key] = this.deepClone(obj[key]); } } return cloned; }, // Pick specific properties pick(obj, keys) { return keys.reduce((result, key) => { if (Object.hasOwn(obj, key)) { result[key] = obj[key]; } return result; }, {}); }, // Omit specific properties omit(obj, keys) { return Object.fromEntries( Object.entries(obj).filter(([key]) => !keys.includes(key)) ); }, // Check if object is empty isEmpty(obj) { return Object.keys(obj).length === 0; } }; // Test the utilities const user = { id: 1, name: "John", email: "john@example.com", password: "secret123", profile: { age: 30, city: "New York" } }; // Deep clone const clonedUser = ObjectUtils.deepClone(user); clonedUser.profile.age = 31; console.log(user.profile.age); // 30 (original unchanged) console.log(clonedUser.profile.age); // 31 // Pick const publicUser = ObjectUtils.pick(user, ["id", "name", "email"]); console.log(publicUser); // { id: 1, name: "John", email: "john@example.com" } // Omit const safeUser = ObjectUtils.omit(user, ["password"]); console.log(safeUser); // { id: 1, name: "John", email: "john@example.com", profile: {...} } // isEmpty console.log(ObjectUtils.isEmpty({})); // true console.log(ObjectUtils.isEmpty(user)); // false

Summary

In this lesson, you learned:

  • Object.keys(), values(), and entries() for iterating objects
  • Object.assign() for merging objects (shallow copy)
  • Object.freeze() and seal() for immutability
  • Object.defineProperty() for precise property control
  • Getters and setters for computed properties
  • Object.create() for prototypal inheritance
  • Object.hasOwn() for property checking
  • Object.is() for equality comparison
  • Object.fromEntries() for converting arrays to objects
Next Up: In the next lesson, we'll explore the powerful Proxy and Reflect APIs for intercepting and customizing object operations!