Object Methods: Mastering the Built-in Toolkit
A professional carpenter's toolbox contains not just basic tools like hammers and saws, but also specialized tools—tape measures for measuring, levels for alignment, and squares for ensuring right angles. Similarly, JavaScript's Object provides a complete set of built-in methods that help us inspect objects, transform data, control properties, and implement complex operations. Mastering these methods is like having a professional toolkit that allows you to handle object data more efficiently.
Object.keys() - Get All Keys
Object.keys() returns an array containing all enumerable property names of an object.
let user = {
name: "Alice",
age: 25,
email: "[email protected]",
city: "New York",
};
let keys = Object.keys(user);
console.log(keys); // ["name", "age", "email", "city"]
// Iterate over all keys of the object
Object.keys(user).forEach((key) => {
console.log(`${key}: ${user[key]}`);
});
// name: Alice
// age: 25
// email: [email protected]
// city: New YorkObject.keys() only returns the object's own properties (excluding inherited properties) and only returns enumerable properties. This makes it the standard method for traversing object properties.
Real-world Application: Validating Object Fields
function validateRequiredFields(obj, requiredFields) {
let actualFields = Object.keys(obj);
let missingFields = requiredFields.filter(
(field) => !actualFields.includes(field)
);
if (missingFields.length > 0) {
return {
valid: false,
missing: missingFields,
};
}
return { valid: true };
}
let userInput = {
username: "johndoe",
email: "[email protected]",
// Missing password
};
let result = validateRequiredFields(userInput, [
"username",
"email",
"password",
]);
console.log(result); // { valid: false, missing: ["password"] }Object Property Counting
let product = {
name: "Laptop",
price: 999,
brand: "TechBrand",
inStock: true,
};
console.log(`This object has ${Object.keys(product).length} properties`);
// This object has 4 properties
// Check if object is empty
function isEmpty(obj) {
return Object.keys(obj).length === 0;
}
console.log(isEmpty({})); // true
console.log(isEmpty(product)); // falseObject.values() - Get All Values
Object.values() returns an array containing all enumerable property values of an object.
let scores = {
math: 95,
english: 88,
science: 92,
history: 85,
};
let values = Object.values(scores);
console.log(values); // [95, 88, 92, 85]
// Calculate average score
let average = values.reduce((sum, score) => sum + score, 0) / values.length;
console.log(`Average score: ${average}`); // Average score: 90Real-world Application: Data Statistics
let inventory = {
laptops: 15,
mice: 50,
keyboards: 30,
monitors: 20,
};
// Total inventory quantity
let totalItems = Object.values(inventory).reduce((sum, qty) => sum + qty, 0);
console.log(`Total items in stock: ${totalItems}`); // 115
// Maximum stock quantity
let maxQuantity = Math.max(...Object.values(inventory));
console.log(`Highest stock quantity: ${maxQuantity}`); // 50
// Find product with most stock
let maxStockProduct = Object.keys(inventory).find(
(key) => inventory[key] === maxQuantity
);
console.log(`Product with most stock: ${maxStockProduct}`); // miceObject.entries() - Get Key-Value Pairs
Object.entries() returns an array containing all enumerable property key-value pairs of an object.
let person = {
name: "Bob",
age: 30,
occupation: "Developer",
};
let entries = Object.entries(person);
console.log(entries);
// [
// ["name", "Bob"],
// ["age", 30],
// ["occupation", "Developer"]
// ]
// Iterate over key-value pairs
for (let [key, value] of Object.entries(person)) {
console.log(`${key}: ${value}`);
}
// name: Bob
// age: 30
// occupation: DeveloperObject.entries() is particularly useful when you need to access both keys and values simultaneously. Combined with destructuring assignment, it's very elegant.
Real-world Application: Object Filtering
function filterObject(obj, predicate) {
return Object.fromEntries(Object.entries(obj).filter(predicate));
}
let products = {
laptop: 999,
mouse: 25,
keyboard: 75,
monitor: 299,
webcam: 89,
};
// Filter products with price below $100
let affordable = filterObject(products, ([key, price]) => price < 100);
console.log(affordable);
// { mouse: 25, keyboard: 75, webcam: 89 }
// Filter products whose names contain "key"
let withKey = filterObject(products, ([name, price]) => name.includes("key"));
console.log(withKey);
// { keyboard: 75 }Object Transformation
let grades = {
Alice: 85,
Bob: 92,
Charlie: 78,
David: 95,
};
// Convert to array format
let gradeList = Object.entries(grades).map(([name, score]) => ({
student: name,
grade: score,
passed: score >= 80,
}));
console.log(gradeList);
// [
// { student: "Alice", grade: 85, passed: true },
// { student: "Bob", grade: 92, passed: true },
// { student: "Charlie", grade: 78, passed: false },
// { student: "David", grade: 95, passed: true }
// ]Object.assign() - Merge Objects
Object.assign() copies all enumerable properties from one or more source objects to the target object.
let target = { a: 1, b: 2 };
let source = { b: 3, c: 4 };
let result = Object.assign(target, source);
console.log(result); // { a: 1, b: 3, c: 4 }
console.log(target); // { a: 1, b: 3, c: 4 } - target object is modified
console.log(target === result); // true - returns the target objectIf there are identical properties, later source objects will override earlier ones. Object.assign() modifies the target object, so if you want to keep the original object unchanged, the first parameter should be an empty object:
let defaults = {
theme: "light",
fontSize: 14,
language: "en",
};
let userSettings = {
theme: "dark",
fontSize: 16,
};
// ❌ This will modify defaults
let settings1 = Object.assign(defaults, userSettings);
console.log(defaults.theme); // "dark" - modified
// ✅ Use empty object as target
let defaults2 = {
theme: "light",
fontSize: 14,
language: "en",
};
let settings2 = Object.assign({}, defaults2, userSettings);
console.log(defaults2.theme); // "light" - unchanged
console.log(settings2); // { theme: "dark", fontSize: 16, language: "en" }Merging Multiple Objects
let base = { x: 1 };
let extra1 = { y: 2 };
let extra2 = { z: 3 };
let combined = Object.assign({}, base, extra1, extra2);
console.log(combined); // { x: 1, y: 2, z: 3 }
// Modern alternative: spread operator (recommended)
let combined2 = { ...base, ...extra1, ...extra2 };
console.log(combined2); // { x: 1, y: 2, z: 3 }Real-world Application: Configuration Merging
function createConfig(userConfig) {
let defaultConfig = {
apiUrl: "https://api.example.com",
timeout: 5000,
retries: 3,
cache: true,
headers: {
"Content-Type": "application/json",
},
};
return Object.assign({}, defaultConfig, userConfig);
}
let config = createConfig({
timeout: 10000,
retries: 5,
});
console.log(config);
// {
// apiUrl: "https://api.example.com",
// timeout: 10000,
// retries: 5,
// cache: true,
// headers: { "Content-Type": "application/json" }
// }Object.freeze() - Freeze Objects
Object.freeze() freezes an object, making its properties unmodifiable, undeletable, and unaddable.
let config = {
appName: "MyApp",
version: "1.0.0",
mode: "production",
};
Object.freeze(config);
// Try to modify - fails silently in strict mode, throws error in non-strict mode
config.mode = "development"; // Invalid
config.newProperty = "test"; // Invalid
delete config.version; // Invalid
console.log(config);
// { appName: "MyApp", version: "1.0.0", mode: "production" }
// Check if object is frozen
console.log(Object.isFrozen(config)); // trueNote that Object.freeze() is a shallow freeze; it only freezes the first level of properties:
let user = {
name: "Alice",
settings: {
theme: "dark",
notifications: true,
},
};
Object.freeze(user);
// ❌ Cannot modify first level
user.name = "Bob"; // Invalid
// ⚠️ Can modify nested objects
user.settings.theme = "light"; // Valid!
console.log(user.settings.theme); // "light"Deep Freezing
To completely freeze an object (including nested objects), you need to recursively freeze:
function deepFreeze(obj) {
// Get all property names
Object.getOwnPropertyNames(obj).forEach((prop) => {
let value = obj[prop];
// If property value is an object and not frozen, recursively freeze
if (value && typeof value === "object" && !Object.isFrozen(value)) {
deepFreeze(value);
}
});
return Object.freeze(obj);
}
let appConfig = {
api: {
baseUrl: "https://api.example.com",
endpoints: {
users: "/users",
posts: "/posts",
},
},
features: {
darkMode: true,
notifications: false,
},
};
deepFreeze(appConfig);
// Now all levels are frozen
appConfig.api.endpoints.users = "/v2/users"; // Invalid
console.log(appConfig.api.endpoints.users); // "/users"Object.seal() - Seal Objects
Object.seal() seals an object, preventing the addition of new properties and deletion of existing properties, but allowing modification of existing property values.
let product = {
name: "Laptop",
price: 999,
inStock: true,
};
Object.seal(product);
// ✅ Can modify existing properties
product.price = 899;
console.log(product.price); // 899
// ❌ Cannot add new properties
product.warranty = "2 years"; // Invalid
// ❌ Cannot delete properties
delete product.inStock; // Invalid
console.log(product);
// { name: "Laptop", price: 899, inStock: true }
// Check if object is sealed
console.log(Object.isSealed(product)); // truefreeze vs seal Comparison
let frozenObj = Object.freeze({ x: 1 });
let sealedObj = Object.seal({ x: 1 });
// freeze: cannot modify values
frozenObj.x = 2;
console.log(frozenObj.x); // 1 - unchanged
// seal: can modify values
sealedObj.x = 2;
console.log(sealedObj.x); // 2 - changed
// Both cannot add new properties
frozenObj.y = 3; // Invalid
sealedObj.y = 3; // Invalid
console.log(Object.isFrozen(frozenObj)); // true
console.log(Object.isSealed(sealedObj)); // true
console.log(Object.isSealed(frozenObj)); // true - frozen objects are also sealed
console.log(Object.isFrozen(sealedObj)); // false - sealed objects are not frozenObject.fromEntries() - Create Objects from Key-Value Pairs
Object.fromEntries() is the inverse operation of Object.entries(), creating objects from arrays of key-value pairs.
let entries = [
["name", "Charlie"],
["age", 28],
["city", "London"],
];
let person = Object.fromEntries(entries);
console.log(person);
// { name: "Charlie", age: 28, city: "London" }Real-world Application: URL Parameter Parsing
function parseQueryString(queryString) {
let params = new URLSearchParams(queryString);
return Object.fromEntries(params);
}
let url = "?name=Alice&age=25&city=New+York";
let parsed = parseQueryString(url);
console.log(parsed);
// { name: "Alice", age: "25", city: "New York" }Map to Object
let userMap = new Map([
["id", 123],
["username", "alice"],
["email", "[email protected]"],
]);
let userObj = Object.fromEntries(userMap);
console.log(userObj);
// { id: 123, username: "alice", email: "[email protected]" }Object Transformation and Filtering
let prices = {
laptop: 999,
mouse: 25,
keyboard: 75,
monitor: 299,
};
// Apply 10% discount to all prices
let discounted = Object.fromEntries(
Object.entries(prices).map(([item, price]) => [item, price * 0.9])
);
console.log(discounted);
// { laptop: 899.1, mouse: 22.5, keyboard: 67.5, monitor: 269.1 }
// Round prices down
let rounded = Object.fromEntries(
Object.entries(discounted).map(([item, price]) => [item, Math.floor(price)])
);
console.log(rounded);
// { laptop: 899, mouse: 22, keyboard: 67, monitor: 269 }Object.create() - Create Objects with Specified Prototype
Object.create() creates a new object, using the specified object as the prototype for the new object.
let animalPrototype = {
eat() {
return `${this.name} is eating`;
},
sleep() {
return `${this.name} is sleeping`;
},
};
let cat = Object.create(animalPrototype);
cat.name = "Whiskers";
cat.sound = "Meow";
console.log(cat.eat()); // "Whiskers is eating"
console.log(cat.sleep()); // "Whiskers is sleeping"
console.log(cat.sound); // "Meow"
let dog = Object.create(animalPrototype);
dog.name = "Buddy";
dog.sound = "Woof";
console.log(dog.eat()); // "Buddy is eating"Creating Pure Objects
Normal objects inherit from Object.prototype. Sometimes we need a completely pure object:
// Normal objects inherit many methods
let normalObj = {};
console.log(normalObj.toString); // function toString() { [native code] }
console.log(normalObj.hasOwnProperty); // function hasOwnProperty() { [native code] }
// Pure objects have no inherited properties
let pureObj = Object.create(null);
console.log(pureObj.toString); // undefined
console.log(pureObj.hasOwnProperty); // undefined
// Pure objects are suitable as pure data dictionaries
pureObj.key1 = "value1";
pureObj.key2 = "value2";Other Useful Methods
Object.hasOwn() - Check Own Properties
New method introduced in ES2022, a safer alternative to hasOwnProperty:
let obj = {
name: "Alice",
age: 25,
};
// Traditional way
console.log(obj.hasOwnProperty("name")); // true
// New way (recommended)
console.log(Object.hasOwn(obj, "name")); // true
console.log(Object.hasOwn(obj, "toString")); // false
// Safer for objects created with Object.create(null)
let pureObj = Object.create(null);
pureObj.key = "value";
// ❌ hasOwnProperty doesn't exist
// pureObj.hasOwnProperty("key"); // TypeError
// ✅ Object.hasOwn works normally
console.log(Object.hasOwn(pureObj, "key")); // trueObject.getOwnPropertyNames() - Get All Property Names
Returns all own property names of an object, including non-enumerable ones:
let obj = {
visible: 1,
};
Object.defineProperty(obj, "hidden", {
value: 2,
enumerable: false,
});
console.log(Object.keys(obj)); // ["visible"]
console.log(Object.getOwnPropertyNames(obj)); // ["visible", "hidden"]Real-world Case Study: Object Utility Library
const ObjectUtils = {
// Deep clone object
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 (let key in obj) {
if (Object.hasOwn(obj, key)) {
cloned[key] = this.deepClone(obj[key]);
}
}
return cloned;
},
// Deep merge objects
deepMerge(target, ...sources) {
if (!sources.length) return target;
const source = sources.shift();
if (this.isObject(target) && this.isObject(source)) {
for (let key in source) {
if (Object.hasOwn(source, key)) {
if (this.isObject(source[key])) {
if (!target[key]) target[key] = {};
this.deepMerge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
}
}
return this.deepMerge(target, ...sources);
},
// Check if it's an object
isObject(item) {
return item && typeof item === "object" && !Array.isArray(item);
},
// Get nested property
getNestedValue(obj, path) {
return path.split(".").reduce((current, key) => current?.[key], obj);
},
// Set nested property
setNestedValue(obj, path, value) {
let keys = path.split(".");
let lastKey = keys.pop();
let target = keys.reduce((current, key) => {
if (!(key in current)) current[key] = {};
return current[key];
}, obj);
target[lastKey] = value;
},
// Remove empty value properties
removeEmpty(obj) {
return Object.fromEntries(
Object.entries(obj).filter(([_, value]) => {
if (value === null || value === undefined || value === "") {
return false;
}
if (this.isObject(value)) {
return Object.keys(this.removeEmpty(value)).length > 0;
}
return true;
})
);
},
// Compare if two objects are equal
isEqual(obj1, obj2) {
if (obj1 === obj2) return true;
if (
!this.isObject(obj1) ||
!this.isObject(obj2) ||
obj1 === null ||
obj2 === null
) {
return false;
}
let keys1 = Object.keys(obj1);
let keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
return keys1.every((key) => {
let val1 = obj1[key];
let val2 = obj2[key];
if (this.isObject(val1) && this.isObject(val2)) {
return this.isEqual(val1, val2);
}
return val1 === val2;
});
},
};
// Usage examples
let original = {
user: {
name: "Alice",
settings: { theme: "dark" },
},
};
let cloned = ObjectUtils.deepClone(original);
cloned.user.settings.theme = "light";
console.log(original.user.settings.theme); // "dark" - unaffected
let base = { a: 1, nested: { x: 1 } };
let override = { b: 2, nested: { y: 2 } };
let merged = ObjectUtils.deepMerge({}, base, override);
console.log(merged); // { a: 1, b: 2, nested: { x: 1, y: 2 } }
let data = { user: { profile: { name: "Bob" } } };
console.log(ObjectUtils.getNestedValue(data, "user.profile.name")); // "Bob"
let dirty = { name: "Alice", email: "", age: null, city: "NYC" };
console.log(ObjectUtils.removeEmpty(dirty)); // { name: "Alice", city: "NYC" }Common Pitfalls and Best Practices
1. Object.assign is Shallow Copy
let original = {
name: "Alice",
settings: { theme: "dark" },
};
let copy = Object.assign({}, original);
copy.settings.theme = "light";
console.log(original.settings.theme); // "light" - modified!
// ✅ Use other methods for deep copy
let deepCopy = JSON.parse(JSON.stringify(original));2. Object.freeze is Shallow
let config = Object.freeze({
api: { url: "https://api.com" },
});
// ❌ Nested objects can still be modified
config.api.url = "https://new-api.com";
console.log(config.api.url); // "https://new-api.com"3. Pay Attention to Method Return Values
let obj1 = { a: 1 };
let obj2 = { b: 2 };
// Object.assign returns the target object (first parameter)
let result = Object.assign(obj1, obj2);
console.log(result === obj1); // true - obj1 is modified and returned
// Object.freeze/seal also return the passed object
let frozen = Object.freeze(obj1);
console.log(frozen === obj1); // trueSummary
JavaScript object methods provide us with a powerful toolkit:
Object.keys/values/entries()- Extract keys, values, or key-value pairs from objectsObject.assign()- Merge objects (shallow copy)Object.freeze()- Completely freeze objects, making them unmodifiableObject.seal()- Seal objects, allowing modification but preventing addition/deletion of propertiesObject.fromEntries()- Create objects from key-value pair arraysObject.create()- Create objects with specified prototypeObject.hasOwn()- Safely check own properties
Mastering these methods helps you:
- Efficiently traverse and transform object data
- Safely merge and clone objects
- Control object mutability
- Implement complex data operations and validation
These methods are fundamental tools for modern JavaScript development. Proficient use of them will greatly improve your code quality and development efficiency.