JavaScript Object Manipulation
Create, clone, merge, and transform JavaScript objects with modern syntax and practical patterns.
TL;DR
- 01Create objects with literals, classes, or
Object.createfor different needs. - 02Clone shallow with spread or deep with
structuredClone. - 03Merge objects easily using spread syntax or
Object.assign.
Tips
- 01Use
structuredClone()instead ofJSON.parse(JSON.stringify(obj))to deep-clone, since it handles dates, maps, and other types correctly. - 02Use
Object.hasOwn(obj, 'key')instead ofhasOwnProperty()directly, since it works safely even on objects created withObject.create(null).
Warnings
- 01Spread and
Object.assign()only do shallow copies, so changes to nested objects in the copy will still affect the original. - 02Checking a property with truthy logic like
if (obj.key)misfires when the value is legitimately0orfalse.
Creating Objects
Object LiteralBuilds an object directly from comma-separated key-value pairs in one line.
const user = { name: 'Ava', age: 28 };classDefines a reusable blueprint with shared methods and a constructor.
class User {
constructor(name) { this.name = name; }
}Object.create()Creates an object that inherits directly from a given prototype object.
const proto = { greet() { return 'Hi!'; } };
const user = Object.create(proto);Computed PropertySets a dynamic key name using bracket syntax inside an object literal.
const key = 'role';
const user = { [key]: 'admin' };Access and Modify
Dot NotationReads or writes a property using a fixed, known key name.
console.log(user.name); // 'Ava'Bracket NotationReads a property using a variable or a name with special characters.
console.log(user['role']); // dynamic key okOptional ChainingSafely reads a deeply nested property without throwing if it's missing.
console.log(user?.address?.city);Nullish CoalescingProvides a fallback value only when the left side is null or undefined.
console.log(user.phone ?? 'N/A');deleteRemoves a property from an object entirely, including its key.
delete user.temp; // removes the propertyEnumerate Properties
Object.keys()Returns an array of an object's own enumerable property names.
Object.keys({ a: 1, b: 2 }); // ['a', 'b']Object.values()Returns an array of just the object's own property values.
Object.values({ a: 1, b: 2 }); // [1, 2]Object.entries()Returns an array of [key, value] pairs for looping or transforms.
Object.entries({ a: 1 }); // [['a', 1]]for...inLoops over enumerable keys, including any inherited from the prototype.
for (const key in user) {
console.log(key);
}Object.hasOwn()Checks whether an object owns a given key, ignoring inherited ones.
Object.hasOwn(user, 'name'); // trueCloning
SpreadCreates a shallow copy with every enumerable own property copied over.
const copy = { ...original };Object.assign()Copies properties into a new target object; also works for merging.
const copy = Object.assign({}, original);structuredClone()Deep-clones nested objects, Dates, Maps, and Sets without shared references.
const deep = structuredClone(original);Merge and Transform
Spread MergeMerges objects into a new one; later keys overwrite earlier matches.
const merged = { ...base, ...extra };Object.assign(target, source)Merges source properties directly into an existing target object in place.
Object.assign(base, extra); // mutates baseObject.fromEntries()Builds an object from an array of [key, value] pairs for fast lookups.
const map = Object.fromEntries(
users.map(u => [u.id, u])
);entries() + map()Converts an object back into an array to transform or filter it.
Object.entries(user).map(([k, v]) => `${k}:${v}`);In Practice
Merges user setting overrides onto defaults, then builds an id-keyed lookup map with Object.fromEntries().
- 01Spread merges
overrideson top ofdefaults, so later keys always win. - 02
Object.fromEntries()turns theusersarray into an id-keyed lookup map. - 03Nullish coalescing (
??) supplies a fallback only when a value is missing. - 04Optional chaining (
?.) readsbyId[9]safely even though that id doesn't exist.
const defaults = { theme: 'light', notifications: true };
const overrides = { theme: 'dark', fontSize: 14 };
const settings = { ...defaults, ...overrides };
const users = [
{ id: 1, name: 'Ava', role: 'admin' },
{ id: 2, name: 'Noah', role: 'editor' },
{ id: 3, name: 'Mia', role: 'viewer' },
];
const byId = Object.fromEntries(users.map(u => [u.id, u]));
console.log(settings.theme); // 'dark'
console.log(settings.fontSize ?? 12); // 14
console.log(byId[2]?.name); // 'Noah'
console.log(byId[9]?.name ?? 'N/A'); // 'N/A'FAQ
Use structuredClone(obj), which natively handles Dates, Maps, Sets, RegExp, and typed arrays. JSON.parse(JSON.stringify()) silently corrupts Dates into strings and drops undefined values entirely.
Both produce a shallow merge, but Object.assign() mutates the target in place, while spread ({...a, ...b}) returns a new object. Prefer spread to avoid modifying an existing object by accident.
Use Object.entries(obj) with for...of to destructure each pair: for (const [key, val] of Object.entries(obj)). Use Object.keys() or Object.values() when you only need one side.
Use computed property syntax directly in the literal: { [variableName]: value }. This is cleaner than creating the object first and then assigning with bracket notation afterward.
Use Object.hasOwn(obj, 'key') for own properties (ES2022), or the in operator to include inherited ones. Avoid relying on truthiness like if (obj.key), since a property can legitimately hold 0, false, or null.