Back to Blog
JavaScriptObjectsMethodsFundamentals

JavaScript Object Methods

Master JavaScript object methods. From Object.keys to Object.fromEntries to property descriptors.

B
Bootspring Team
Engineering
December 7, 2020
7 min read

JavaScript provides powerful methods for working with objects. Here's a comprehensive guide.

Object.keys, values, entries#

1const user = { 2 name: 'Alice', 3 age: 30, 4 email: 'alice@example.com', 5}; 6 7// Get keys 8const keys = Object.keys(user); 9console.log(keys); // ['name', 'age', 'email'] 10 11// Get values 12const values = Object.values(user); 13console.log(values); // ['Alice', 30, 'alice@example.com'] 14 15// Get entries (key-value pairs) 16const entries = Object.entries(user); 17console.log(entries); 18// [['name', 'Alice'], ['age', 30], ['email', 'alice@example.com']] 19 20// Iterate with entries 21for (const [key, value] of Object.entries(user)) { 22 console.log(`${key}: ${value}`); 23} 24 25// Transform object 26const upperKeys = Object.fromEntries( 27 Object.entries(user).map(([key, value]) => [key.toUpperCase(), value]) 28); 29// { NAME: 'Alice', AGE: 30, EMAIL: 'alice@example.com' }

Object.assign#

1// Shallow copy 2const original = { a: 1, b: 2 }; 3const copy = Object.assign({}, original); 4 5// Merge objects 6const target = { a: 1, b: 2 }; 7const source = { b: 3, c: 4 }; 8const result = Object.assign(target, source); 9console.log(result); // { a: 1, b: 3, c: 4 } 10console.log(target === result); // true (mutates target) 11 12// Multiple sources 13const merged = Object.assign({}, obj1, obj2, obj3); 14 15// With defaults 16function configure(options) { 17 const defaults = { 18 debug: false, 19 timeout: 1000, 20 }; 21 return Object.assign({}, defaults, options); 22} 23 24// Shallow copy caveat 25const nested = { 26 a: 1, 27 b: { c: 2 }, 28}; 29const shallowCopy = Object.assign({}, nested); 30shallowCopy.b.c = 100; 31console.log(nested.b.c); // 100 (same reference!)

Object.fromEntries#

1// Convert entries to object 2const entries = [ 3 ['name', 'Alice'], 4 ['age', 30], 5]; 6const obj = Object.fromEntries(entries); 7// { name: 'Alice', age: 30 } 8 9// From Map 10const map = new Map([ 11 ['foo', 'bar'], 12 ['baz', 42], 13]); 14const objFromMap = Object.fromEntries(map); 15// { foo: 'bar', baz: 42 } 16 17// Transform object values 18const prices = { 19 apple: 1.5, 20 banana: 0.75, 21 orange: 2.0, 22}; 23 24const discounted = Object.fromEntries( 25 Object.entries(prices).map(([item, price]) => [item, price * 0.9]) 26); 27// { apple: 1.35, banana: 0.675, orange: 1.8 } 28 29// Filter object 30const user = { 31 name: 'Alice', 32 age: 30, 33 password: 'secret', 34 email: 'alice@example.com', 35}; 36 37const safe = Object.fromEntries( 38 Object.entries(user).filter(([key]) => key !== 'password') 39); 40// { name: 'Alice', age: 30, email: 'alice@example.com' }

Property Descriptors#

1// Get descriptor 2const obj = { name: 'Alice' }; 3const descriptor = Object.getOwnPropertyDescriptor(obj, 'name'); 4console.log(descriptor); 5// { 6// value: 'Alice', 7// writable: true, 8// enumerable: true, 9// configurable: true 10// } 11 12// Define property with descriptor 13Object.defineProperty(obj, 'id', { 14 value: 1, 15 writable: false, 16 enumerable: true, 17 configurable: false, 18}); 19 20obj.id = 2; // Fails silently (or throws in strict mode) 21console.log(obj.id); // 1 22 23// Define multiple properties 24Object.defineProperties(obj, { 25 firstName: { 26 value: 'Alice', 27 writable: true, 28 }, 29 lastName: { 30 value: 'Smith', 31 writable: true, 32 }, 33}); 34 35// Getter and setter 36Object.defineProperty(obj, 'fullName', { 37 get() { 38 return `${this.firstName} ${this.lastName}`; 39 }, 40 set(value) { 41 const [first, last] = value.split(' '); 42 this.firstName = first; 43 this.lastName = last; 44 }, 45 enumerable: true, 46}); 47 48console.log(obj.fullName); // 'Alice Smith' 49obj.fullName = 'Bob Jones'; 50console.log(obj.firstName); // 'Bob'

Object.freeze, seal, preventExtensions#

1// Freeze - no changes at all 2const frozen = Object.freeze({ 3 name: 'Alice', 4 address: { city: 'NYC' }, 5}); 6 7frozen.name = 'Bob'; // Fails silently 8frozen.age = 30; // Fails silently 9delete frozen.name; // Fails silently 10frozen.address.city = 'LA'; // Works! (shallow freeze) 11 12console.log(Object.isFrozen(frozen)); // true 13 14// Deep freeze 15function deepFreeze(obj) { 16 Object.keys(obj).forEach(key => { 17 if (typeof obj[key] === 'object' && obj[key] !== null) { 18 deepFreeze(obj[key]); 19 } 20 }); 21 return Object.freeze(obj); 22} 23 24// Seal - can modify, but not add/delete 25const sealed = Object.seal({ 26 name: 'Alice', 27 age: 30, 28}); 29 30sealed.name = 'Bob'; // Works 31sealed.email = 'x'; // Fails silently 32delete sealed.age; // Fails silently 33 34console.log(Object.isSealed(sealed)); // true 35 36// Prevent extensions - can modify/delete, but not add 37const obj = { name: 'Alice' }; 38Object.preventExtensions(obj); 39 40obj.name = 'Bob'; // Works 41delete obj.name; // Works 42obj.age = 30; // Fails silently 43 44console.log(Object.isExtensible(obj)); // false

Object.create#

1// Create with prototype 2const animal = { 3 speak() { 4 console.log(`${this.name} makes a sound`); 5 }, 6}; 7 8const dog = Object.create(animal); 9dog.name = 'Buddy'; 10dog.speak(); // 'Buddy makes a sound' 11 12// Create with property descriptors 13const person = Object.create(Object.prototype, { 14 name: { 15 value: 'Alice', 16 writable: true, 17 enumerable: true, 18 }, 19 greet: { 20 value: function () { 21 return `Hello, I'm ${this.name}`; 22 }, 23 enumerable: true, 24 }, 25}); 26 27// Create with null prototype (no inherited methods) 28const dict = Object.create(null); 29dict.foo = 'bar'; 30console.log(dict.toString); // undefined (no inherited methods) 31 32// Useful for lookup maps 33const lookup = Object.create(null); 34lookup['hasOwnProperty'] = 'safe'; // No collision with Object.prototype

Object.getPrototypeOf, setPrototypeOf#

1// Get prototype 2const arr = []; 3console.log(Object.getPrototypeOf(arr) === Array.prototype); // true 4 5class Animal {} 6class Dog extends Animal {} 7const dog = new Dog(); 8 9console.log(Object.getPrototypeOf(dog) === Dog.prototype); // true 10console.log(Object.getPrototypeOf(Dog.prototype) === Animal.prototype); // true 11 12// Set prototype (not recommended for performance) 13const obj = { a: 1 }; 14const proto = { b: 2 }; 15Object.setPrototypeOf(obj, proto); 16 17console.log(obj.b); // 2 18 19// Check prototype chain 20console.log(proto.isPrototypeOf(obj)); // true

Object.is#

1// Strict equality with special cases 2console.log(Object.is(1, 1)); // true 3console.log(Object.is('a', 'a')); // true 4console.log(Object.is({}, {})); // false (different references) 5 6// Different from === 7console.log(NaN === NaN); // false 8console.log(Object.is(NaN, NaN)); // true 9 10console.log(-0 === 0); // true 11console.log(Object.is(-0, 0)); // false 12 13// Use cases 14function sameValue(a, b) { 15 return Object.is(a, b); 16} 17 18// In React, used for shallow comparison 19function shallowEqual(obj1, obj2) { 20 const keys1 = Object.keys(obj1); 21 const keys2 = Object.keys(obj2); 22 23 if (keys1.length !== keys2.length) return false; 24 25 for (const key of keys1) { 26 if (!Object.is(obj1[key], obj2[key])) return false; 27 } 28 29 return true; 30}

Object.hasOwn#

1// Modern way to check own properties 2const obj = { name: 'Alice' }; 3 4console.log(Object.hasOwn(obj, 'name')); // true 5console.log(Object.hasOwn(obj, 'toString')); // false (inherited) 6 7// Better than hasOwnProperty 8const dict = Object.create(null); 9dict.key = 'value'; 10 11// This would throw 12// dict.hasOwnProperty('key') 13 14// This works 15Object.hasOwn(dict, 'key'); // true 16 17// Also works with overridden hasOwnProperty 18const tricky = { 19 hasOwnProperty: () => false, 20 name: 'Alice', 21}; 22 23console.log(tricky.hasOwnProperty('name')); // false (wrong!) 24console.log(Object.hasOwn(tricky, 'name')); // true (correct)

Object.getOwnPropertyNames/Symbols#

1const sym = Symbol('hidden'); 2 3const obj = { 4 name: 'Alice', 5 age: 30, 6 [sym]: 'secret', 7}; 8 9// Get all string-keyed properties (including non-enumerable) 10console.log(Object.getOwnPropertyNames(obj)); 11// ['name', 'age'] 12 13// Get all symbol-keyed properties 14console.log(Object.getOwnPropertySymbols(obj)); 15// [Symbol(hidden)] 16 17// Get all properties 18const allKeys = [ 19 ...Object.getOwnPropertyNames(obj), 20 ...Object.getOwnPropertySymbols(obj), 21]; 22// ['name', 'age', Symbol(hidden)] 23 24// Or use Reflect.ownKeys 25console.log(Reflect.ownKeys(obj)); 26// ['name', 'age', Symbol(hidden)] 27 28// Get all descriptors 29const descriptors = Object.getOwnPropertyDescriptors(obj);

Practical Patterns#

1// Pick specific properties 2function pick(obj, keys) { 3 return Object.fromEntries( 4 keys.filter(key => key in obj).map(key => [key, obj[key]]) 5 ); 6} 7 8const user = { name: 'Alice', age: 30, email: 'a@b.com', password: 'x' }; 9const safe = pick(user, ['name', 'age', 'email']); 10 11// Omit specific properties 12function omit(obj, keys) { 13 return Object.fromEntries( 14 Object.entries(obj).filter(([key]) => !keys.includes(key)) 15 ); 16} 17 18const withoutPassword = omit(user, ['password']); 19 20// Map object values 21function mapValues(obj, fn) { 22 return Object.fromEntries( 23 Object.entries(obj).map(([key, value]) => [key, fn(value, key)]) 24 ); 25} 26 27const doubled = mapValues({ a: 1, b: 2 }, x => x * 2); 28// { a: 2, b: 4 } 29 30// Group by property 31function groupBy(arr, key) { 32 return arr.reduce((acc, item) => { 33 const group = item[key]; 34 acc[group] = acc[group] || []; 35 acc[group].push(item); 36 return acc; 37 }, {}); 38} 39 40const users = [ 41 { name: 'Alice', role: 'admin' }, 42 { name: 'Bob', role: 'user' }, 43 { name: 'Charlie', role: 'admin' }, 44]; 45 46const byRole = groupBy(users, 'role'); 47// { admin: [...], user: [...] }

Best Practices#

Immutability: ✓ Use Object.assign({}, obj) or spread ✓ Use Object.freeze for constants ✓ Deep clone when needed ✓ Consider immutable libraries Property Access: ✓ Use Object.hasOwn over hasOwnProperty ✓ Use optional chaining for nested access ✓ Check for null/undefined ✓ Use default values Iteration: ✓ Use Object.entries for key-value ✓ Use Object.keys for keys only ✓ Consider for...in with hasOwn ✓ Use Object.fromEntries to reconstruct Performance: ✓ Avoid Object.setPrototypeOf ✓ Cache Object.keys results ✓ Use Map for frequent additions ✓ Consider frozen objects for constants

Conclusion#

JavaScript's object methods provide powerful tools for manipulation, iteration, and protection. Use Object.entries/fromEntries for transformations, property descriptors for fine-grained control, and Object.freeze/seal for immutability. Modern methods like Object.hasOwn offer safer alternatives to older patterns.

Share this article

Help spread the word about Bootspring