Skip to content

Object Methods and Utilities

Objects are the fundamental building blocks of JavaScript. This comprehensive guide covers all object methods, property manipulation techniques, and advanced patterns for working with objects effectively.

// Object literal (most common)
const person = {
name: 'Alice',
age: 30,
city: 'New York',
greet() {
return `Hello, I'm ${this.name}`;
}
};
// Object constructor
const user = new Object();
user.name = 'Bob';
user.age = 25;
// Object.create() - specify prototype
const protoObject = {
greet() {
return `Hello from ${this.name}`;
}
};
const child = Object.create(protoObject);
child.name = 'Charlie';
// Object.create(null) - no prototype chain
const pureObject = Object.create(null);
pureObject.data = 'value';
console.log(pureObject.toString); // undefined
// Factory function pattern
function createPerson(name, age, city) {
return {
name,
age,
city,
greet() {
return `Hello, I'm ${this.name} from ${this.city}`;
},
getAge() {
return this.age;
},
haveBirthday() {
this.age++;
return this.age;
}
};
}
const alice = createPerson('Alice', 30, 'New York');
const bob = createPerson('Bob', 25, 'Los Angeles');
// Constructor function pattern
function Person(name, age, city) {
this.name = name;
this.age = age;
this.city = city;
}
Person.prototype.greet = function() {
return `Hello, I'm ${this.name}`;
};
Person.prototype.haveBirthday = function() {
this.age++;
return this.age;
};
const person1 = new Person('David', 35, 'Chicago');
const person2 = new Person('Eve', 28, 'Seattle');
// Class syntax (ES6+)
class ModernPerson {
constructor(name, age, city) {
this.name = name;
this.age = age;
this.city = city;
}
greet() {
return `Hello, I'm ${this.name}`;
}
haveBirthday() {
this.age++;
return this.age;
}
static species() {
return 'Homo sapiens';
}
}
const modern1 = new ModernPerson('Frank', 40, 'Miami');
const obj = {};
// Simple property assignment
obj.simpleProperty = 'value';
// Object.defineProperty() - detailed control
Object.defineProperty(obj, 'configuredProperty', {
value: 'configured value',
writable: true, // Can be changed
enumerable: true, // Shows in for...in and Object.keys()
configurable: true // Can be deleted or reconfigured
});
// Read-only property
Object.defineProperty(obj, 'readOnly', {
value: 'cannot change',
writable: false,
enumerable: true,
configurable: false
});
// Hidden property (non-enumerable)
Object.defineProperty(obj, 'hidden', {
value: 'secret',
writable: true,
enumerable: false, // Won't appear in Object.keys()
configurable: true
});
// Getter and setter properties
Object.defineProperty(obj, 'computed', {
get() {
return this._computed || 'default';
},
set(value) {
this._computed = value.toUpperCase();
},
enumerable: true,
configurable: true
});
// Object.defineProperties() - define multiple properties
Object.defineProperties(obj, {
prop1: {
value: 'value1',
writable: true
},
prop2: {
value: 'value2',
writable: false
},
prop3: {
get() { return this._prop3; },
set(value) { this._prop3 = value * 2; }
}
});
// Get property descriptor
const descriptor = Object.getOwnPropertyDescriptor(obj, 'readOnly');
console.log(descriptor);
// { value: 'cannot change', writable: false, enumerable: true, configurable: false }
// Get all property descriptors
const allDescriptors = Object.getOwnPropertyDescriptors(obj);
console.log(allDescriptors);
const obj = {
own1: 'value1',
own2: 'value2',
own3: 'value3'
};
// Add non-enumerable property
Object.defineProperty(obj, 'hidden', {
value: 'hidden value',
enumerable: false
});
// Add property to prototype
Object.prototype.inherited = 'inherited value';
// Object.keys() - own enumerable string keys
const keys = Object.keys(obj);
console.log(keys); // ['own1', 'own2', 'own3']
// Object.values() - own enumerable values
const values = Object.values(obj);
console.log(values); // ['value1', 'value2', 'value3']
// Object.entries() - own enumerable key-value pairs
const entries = Object.entries(obj);
console.log(entries); // [['own1', 'value1'], ['own2', 'value2'], ['own3', 'value3']]
// Object.getOwnPropertyNames() - all own string keys (including non-enumerable)
const allKeys = Object.getOwnPropertyNames(obj);
console.log(allKeys); // ['own1', 'own2', 'own3', 'hidden']
// Object.getOwnPropertySymbols() - own symbol keys
const sym1 = Symbol('symbol1');
const sym2 = Symbol('symbol2');
obj[sym1] = 'symbol value 1';
obj[sym2] = 'symbol value 2';
const symbolKeys = Object.getOwnPropertySymbols(obj);
console.log(symbolKeys); // [Symbol(symbol1), Symbol(symbol2)]
// Reflect.ownKeys() - all own keys (strings and symbols)
const allOwnKeys = Reflect.ownKeys(obj);
console.log(allOwnKeys); // ['own1', 'own2', 'own3', 'hidden', Symbol(symbol1), Symbol(symbol2)]
// for...in loop - enumerable properties (including inherited)
for (const key in obj) {
console.log(key); // own1, own2, own3, inherited
}
// for...in with hasOwnProperty check
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
console.log(key); // own1, own2, own3
}
}
// Clean up prototype pollution example
delete Object.prototype.inherited;
const obj = {
prop1: 'value1',
prop2: null,
prop3: undefined,
prop4: 0,
prop5: false,
prop6: ''
};
// hasOwnProperty() - check own property
console.log(obj.hasOwnProperty('prop1')); // true
console.log(obj.hasOwnProperty('toString')); // false (inherited)
// Object.hasOwn() - ES2022 alternative to hasOwnProperty
console.log(Object.hasOwn(obj, 'prop1')); // true
console.log(Object.hasOwn(obj, 'toString')); // false
// in operator - check property (including inherited)
console.log('prop1' in obj); // true
console.log('toString' in obj); // true (inherited from Object.prototype)
// propertyIsEnumerable() - check if property is enumerable
console.log(obj.propertyIsEnumerable('prop1')); // true
Object.defineProperty(obj, 'nonEnum', {
value: 'hidden',
enumerable: false
});
console.log(obj.propertyIsEnumerable('nonEnum')); // false
// Difference between undefined and non-existent
console.log(obj.prop3); // undefined
console.log(obj.nonExistent); // undefined
console.log('prop3' in obj); // true
console.log('nonExistent' in obj); // false
// Safe property access patterns
function safeGet(obj, path, defaultValue = undefined) {
const keys = path.split('.');
let current = obj;
for (const key of keys) {
if (current == null || !(key in current)) {
return defaultValue;
}
current = current[key];
}
return current;
}
const nested = {
user: {
profile: {
name: 'Alice',
settings: {
theme: 'dark'
}
}
}
};
console.log(safeGet(nested, 'user.profile.name')); // 'Alice'
console.log(safeGet(nested, 'user.profile.age', 'unknown')); // 'unknown'
console.log(safeGet(nested, 'user.invalid.path', 'default')); // 'default'
// Optional chaining (ES2020)
console.log(nested.user?.profile?.name); // 'Alice'
console.log(nested.user?.profile?.age); // undefined
console.log(nested.user?.invalid?.path); // undefined
// Shallow copying
const original = {
name: 'Alice',
age: 30,
hobbies: ['reading', 'swimming'],
address: {
city: 'New York',
zip: '10001'
}
};
// Object.assign() - shallow copy
const copy1 = Object.assign({}, original);
// Spread operator - shallow copy
const copy2 = { ...original };
// Both shallow copies share nested objects
copy1.hobbies.push('cycling');
console.log(original.hobbies); // ['reading', 'swimming', 'cycling']
// Deep copying solutions
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') return obj;
if (obj instanceof Date) return new Date(obj.getTime());
if (obj instanceof Array) return obj.map(deepClone);
if (obj instanceof Object) {
const cloned = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
cloned[key] = deepClone(obj[key]);
}
}
return cloned;
}
}
// Modern deep cloning (where supported)
const deepCopy = structuredClone(original);
// JSON method (limited - no functions, dates become strings, etc.)
const jsonCopy = JSON.parse(JSON.stringify(original));
// Object merging with Object.assign()
const defaults = {
theme: 'light',
language: 'en',
notifications: true,
volume: 50
};
const userPrefs = {
theme: 'dark',
volume: 75
};
const settings = Object.assign({}, defaults, userPrefs);
// { theme: 'dark', language: 'en', notifications: true, volume: 75 }
// Spread operator merging
const spreadSettings = { ...defaults, ...userPrefs };
// Deep merging function
function deepMerge(target, ...sources) {
if (!sources.length) return target;
const source = sources.shift();
if (isObject(target) && isObject(source)) {
for (const key in source) {
if (isObject(source[key])) {
if (!target[key]) Object.assign(target, { [key]: {} });
deepMerge(target[key], source[key]);
} else {
Object.assign(target, { [key]: source[key] });
}
}
}
return deepMerge(target, ...sources);
}
function isObject(item) {
return item && typeof item === 'object' && !Array.isArray(item);
}
const obj1 = {
a: 1,
b: {
c: 2,
d: 3
}
};
const obj2 = {
b: {
d: 4,
e: 5
},
f: 6
};
const merged = deepMerge({}, obj1, obj2);
// { a: 1, b: { c: 2, d: 4, e: 5 }, f: 6 }
// Transform object keys
function transformKeys(obj, transformer) {
return Object.keys(obj).reduce((result, key) => {
result[transformer(key)] = obj[key];
return result;
}, {});
}
// Transform object values
function transformValues(obj, transformer) {
return Object.keys(obj).reduce((result, key) => {
result[key] = transformer(obj[key], key);
return result;
}, {});
}
// Pick specific properties
function pick(obj, keys) {
return keys.reduce((result, key) => {
if (key in obj) {
result[key] = obj[key];
}
return result;
}, {});
}
// Omit specific properties
function omit(obj, keys) {
const omitSet = new Set(keys);
return Object.keys(obj).reduce((result, key) => {
if (!omitSet.has(key)) {
result[key] = obj[key];
}
return result;
}, {});
}
// Compact object (remove falsy values)
function compact(obj) {
return Object.keys(obj).reduce((result, key) => {
if (obj[key]) {
result[key] = obj[key];
}
return result;
}, {});
}
// Invert object (swap keys and values)
function invert(obj) {
return Object.keys(obj).reduce((result, key) => {
result[obj[key]] = key;
return result;
}, {});
}
// Group object entries by value
function groupBy(obj, grouper) {
return Object.keys(obj).reduce((result, key) => {
const group = grouper(obj[key], key);
if (!result[group]) {
result[group] = {};
}
result[group][key] = obj[key];
return result;
}, {});
}
// Usage examples
const data = {
firstName: 'Alice',
lastName: 'Johnson',
age: 30,
city: 'New York',
country: 'USA',
isActive: true,
score: null,
notes: ''
};
// Convert to camelCase keys
const camelCased = transformKeys(data, key =>
key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase())
);
// Double numeric values
const doubled = transformValues(data, (value, key) =>
typeof value === 'number' ? value * 2 : value
);
// Pick only name-related fields
const nameOnly = pick(data, ['firstName', 'lastName']);
// { firstName: 'Alice', lastName: 'Johnson' }
// Omit location fields
const withoutLocation = omit(data, ['city', 'country']);
// Remove falsy values
const compacted = compact(data);
// { firstName: 'Alice', lastName: 'Johnson', age: 30, city: 'New York', country: 'USA', isActive: true }
// Invert simple object
const statusCodes = { ok: 200, notFound: 404, error: 500 };
const codeToStatus = invert(statusCodes);
// { '200': 'ok', '404': 'notFound', '500': 'error' }
// Group by data type
const grouped = groupBy(data, (value) => typeof value);
// { string: { firstName: 'Alice', ... }, number: { age: 30 }, boolean: { isActive: true }, object: { score: null, notes: '' } }
// Object.freeze() - prevent all modifications
const frozen = Object.freeze({
name: 'Alice',
age: 30,
hobbies: ['reading', 'swimming']
});
// These operations will fail silently in non-strict mode, throw in strict mode
// frozen.name = 'Bob'; // Cannot modify
// frozen.city = 'New York'; // Cannot add
// delete frozen.age; // Cannot delete
console.log(Object.isFrozen(frozen)); // true
// Note: freeze is shallow
frozen.hobbies.push('cycling'); // This works! Array is not frozen
// Deep freeze implementation
function deepFreeze(obj) {
Object.getOwnPropertyNames(obj).forEach(prop => {
if (obj[prop] && typeof obj[prop] === 'object') {
deepFreeze(obj[prop]);
}
});
return Object.freeze(obj);
}
const deepFrozen = deepFreeze({
name: 'Alice',
hobbies: ['reading', 'swimming'],
address: {
city: 'New York',
coordinates: { lat: 40.7128, lng: -74.0060 }
}
});
// Object.seal() - prevent property addition/deletion, allow modification
const sealed = Object.seal({
name: 'Bob',
age: 25
});
sealed.name = 'Robert'; // This works
// sealed.city = 'Boston'; // Cannot add new properties
// delete sealed.age; // Cannot delete properties
console.log(Object.isSealed(sealed)); // true
// Object.preventExtensions() - prevent property addition only
const extensionPrevented = Object.preventExtensions({
name: 'Charlie',
age: 35
});
extensionPrevented.name = 'Charles'; // This works
delete extensionPrevented.age; // This works
// extensionPrevented.city = 'LA'; // Cannot add new properties
console.log(Object.isExtensible(extensionPrevented)); // false
// Immutable update patterns
function updateObject(obj, updates) {
return { ...obj, ...updates };
}
function updateNested(obj, path, value) {
const keys = path.split('.');
const [head, ...tail] = keys;
if (tail.length === 0) {
return { ...obj, [head]: value };
}
return {
...obj,
[head]: updateNested(obj[head] || {}, tail.join('.'), value)
};
}
const user = {
name: 'Alice',
profile: {
settings: {
theme: 'light',
notifications: true
}
}
};
const updatedUser = updateNested(user, 'profile.settings.theme', 'dark');
// Original user object is unchanged
console.log(user.profile.settings.theme); // 'light'
console.log(updatedUser.profile.settings.theme); // 'dark'
// Basic Proxy example
const target = {
name: 'Alice',
age: 30
};
const proxy = new Proxy(target, {
get(obj, prop) {
console.log(`Getting property: ${prop}`);
return obj[prop];
},
set(obj, prop, value) {
console.log(`Setting property: ${prop} = ${value}`);
obj[prop] = value;
return true;
},
has(obj, prop) {
console.log(`Checking property: ${prop}`);
return prop in obj;
}
});
proxy.name; // Logs: "Getting property: name"
proxy.city = 'NY'; // Logs: "Setting property: city = NY"
'age' in proxy; // Logs: "Checking property: age"
// Validation proxy
function createValidatedObject(schema) {
return new Proxy({}, {
set(obj, prop, value) {
const validator = schema[prop];
if (validator && !validator(value)) {
throw new Error(`Invalid value for property ${prop}: ${value}`);
}
obj[prop] = value;
return true;
}
});
}
const userSchema = {
name: value => typeof value === 'string' && value.length > 0,
age: value => typeof value === 'number' && value >= 0,
email: value => typeof value === 'string' && value.includes('@')
};
const validatedUser = createValidatedObject(userSchema);
validatedUser.name = 'Alice'; // OK
validatedUser.age = 30; // OK
// validatedUser.age = -5; // Throws error
// validatedUser.email = 'invalid'; // Throws error
// Default values proxy
function withDefaults(target, defaults) {
return new Proxy(target, {
get(obj, prop) {
return prop in obj ? obj[prop] : defaults[prop];
}
});
}
const config = withDefaults({
port: 3000
}, {
host: 'localhost',
port: 8080,
ssl: false
});
console.log(config.port); // 3000 (from target)
console.log(config.host); // 'localhost' (from defaults)
console.log(config.ssl); // false (from defaults)
// Observable proxy
function createObservable(target, onChange) {
return new Proxy(target, {
set(obj, prop, value) {
const oldValue = obj[prop];
obj[prop] = value;
onChange(prop, value, oldValue);
return true;
}
});
}
const observable = createObservable({ count: 0 }, (prop, newVal, oldVal) => {
console.log(`${prop} changed from ${oldVal} to ${newVal}`);
});
observable.count = 1; // Logs: "count changed from 0 to 1"
observable.count = 2; // Logs: "count changed from 1 to 2"
// Revocable proxy
const { proxy: revocableProxy, revoke } = Proxy.revocable(target, {
get(obj, prop) {
return obj[prop];
}
});
console.log(revocableProxy.name); // 'Alice'
revoke();
// console.log(revocableProxy.name); // Throws TypeError: Cannot perform 'get' on a proxy that has been revoked
// Advanced destructuring patterns
const user = {
id: 1,
name: 'Alice Johnson',
email: 'alice@example.com',
profile: {
bio: 'Software developer',
location: 'New York',
skills: ['JavaScript', 'React', 'Node.js'],
social: {
github: 'alicejohnson',
twitter: '@alice_codes'
}
},
preferences: {
theme: 'dark',
notifications: {
email: true,
push: false,
sms: true
}
}
};
// Basic destructuring with renaming and defaults
const {
name: fullName,
email: emailAddress = 'no-email@example.com',
phone = 'No phone provided'
} = user;
// Nested destructuring
const {
profile: {
bio,
location,
skills: [primarySkill, ...otherSkills],
social: { github, twitter }
}
} = user;
// Deep nested destructuring
const {
preferences: {
theme,
notifications: { email: emailNotifs, push: pushNotifs }
}
} = user;
// Rest properties
const { id, name, ...userWithoutIdAndName } = user;
// Destructuring in function parameters
function greetUser({ name, profile: { location } }) {
return `Hello ${name} from ${location}!`;
}
console.log(greetUser(user)); // "Hello Alice Johnson from New York!"
// Destructuring with computed property names
const propName = 'email';
const { [propName]: userEmail } = user;
// Pattern matching utility
function matchPattern(obj, pattern) {
function match(obj, pattern, path = '') {
if (pattern === obj) return true;
if (typeof pattern !== 'object' || pattern === null) return false;
if (typeof obj !== 'object' || obj === null) return false;
for (const key in pattern) {
const currentPath = path ? `${path}.${key}` : key;
if (!(key in obj)) return false;
if (!match(obj[key], pattern[key], currentPath)) return false;
}
return true;
}
return match(obj, pattern);
}
// Usage
const pattern1 = { name: 'Alice Johnson' };
const pattern2 = { profile: { location: 'New York' } };
const pattern3 = { profile: { skills: ['JavaScript'] } }; // This won't match exactly
console.log(matchPattern(user, pattern1)); // true
console.log(matchPattern(user, pattern2)); // true
console.log(matchPattern(user, pattern3)); // false (arrays don't match exactly)
// Extract values by pattern
function extractByPattern(obj, pattern) {
const result = {};
function extract(obj, pattern, target, path = '') {
for (const key in pattern) {
const currentPath = path ? `${path}.${key}` : key;
if (key in obj) {
if (typeof pattern[key] === 'object' && pattern[key] !== null && !Array.isArray(pattern[key])) {
target[key] = {};
extract(obj[key], pattern[key], target[key], currentPath);
} else {
target[key] = obj[key];
}
}
}
}
extract(obj, pattern, result);
return result;
}
const extracted = extractByPattern(user, {
name: true,
profile: {
bio: true,
skills: true
}
});
// { name: 'Alice Johnson', profile: { bio: 'Software developer', skills: [...] } }

This comprehensive guide covers the vast majority of object operations and patterns in JavaScript. Objects are central to JavaScript programming, and mastering these techniques will greatly enhance your ability to work with complex data structures and implement sophisticated programming patterns.


Practice these object methods and patterns to become proficient in JavaScript object manipulation and advanced programming techniques.