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!