https://bigfrontend.dev/problem/implement-object-assign
The Object.assign()
method copies all enumerable own properties from one or more source objects to a target object. It returns the target object. (source: MDN)
It is widely used, Object Spread operator actually is internally the same as Object.assign()
(source). Following 2 lines of code are totally the same.
let aClone = { ...a };
let aClone = Object.assign({}, a);
This is an easy one, could you implement Object.assign()
with your own implementation?
/**
* @param {any} target
* @param {any[]} sources
* @return {object}
*/
function objectAssign(target, ...sources) {
if (!target) {
throw new Error();
}
if (typeof target !== 'object') {
const constructor = Object.getPrototypeOf(target).constructor;
target = new constructor(target);
}
for (const source of sources) {
if (!source) {
continue;
}
const keys = [
...Object.keys(source),
...Object.getOwnPropertySymbols(source),
];
for (const key of keys) {
const descriptor = Object.getOwnPropertyDescriptor(target, key);
if (descriptor && !descriptor.configurable) {
throw new Error();
}
target[key] = source[key];
}
}
return target;
}
const obj = { a: 1 };
const copy = objectAssign({}, obj);
console.log(copy); // { a: 1 }
console.log(copy === obj); // false
console.log(Object.getPrototypeOf(copy) === Object.getPrototypeOf(obj)); // true
console.log(Object.getOwnPropertyDescriptor(copy, "a")); // { value: 1, writable: true, enumerable: true, configurable: true }
console.log(Object.getOwnPropertyDescriptor(obj, "a")); // { value: 1, writable: true, enumerable: true, configurable: true }
console.log(Object.getOwnPropertyDescriptor(copy, "b")); // undefined
console.log(Object.getOwnPropertyDescriptor(obj, "b")); // undefined
Let's break down the objectAssign
function and its usage:
-
Function Definition: The
objectAssign
function is defined to take atarget
object and any number ofsources
objects. It's designed to mimic the behavior of the built-inObject.assign
method in JavaScript. -
Error Checking: If
target
is not provided or isnull
, it throws an error. Iftarget
is not an object, it creates a new instance oftarget
's prototype's constructor and assigns it totarget
. -
Iterating Over Sources: It then iterates over each
source
insources
. If asource
is not provided or isnull
, it skips to the nextsource
. -
Getting Keys: For each
source
, it gets an array of its own enumerable property keys and symbols. -
Copying Properties: It then iterates over each key in
keys
. Iftarget
has a non-configurable own property with the same key, it throws an error. Otherwise, it assigns the value of thesource
property to thetarget
property. -
Return Value: After all
sources
have been processed, it returnstarget
. -
Usage: The function is then used to create a shallow copy of an object
obj
. The copy is logged to the console, showing that it has the same properties asobj
. It also logs thatcopy
is not the same object asobj
, that they have the same prototype, and that they have the same property descriptors for "a" and no property descriptor for "b".
This objectAssign
function provides a way to copy properties from one or more source objects to a target object. It throws an error if the target object is not provided or if a non-configurable property of the target object would be overwritten.
Here are some real-world examples of how Object.assign
can be used:
const userBasicInfo = {
name: 'John Doe',
age: 30
};
const userContactInfo = {
email: '[email protected]',
phone: '123-456-7890'
};
const userProfile = Object.assign({}, userBasicInfo, userContactInfo);
console.log(userProfile);
// Output: { name: 'John Doe', age: 30, email: 'john.doe@example.com', phone: '123-456-7890' }
const originalObject = {
name: 'Jane Smith',
profession: 'Software Developer'
};
const clonedObject = Object.assign({}, originalObject);
clonedObject.profession = 'Senior Software Developer';
console.log(originalObject.profession); // Output: 'Software Developer'
console.log(clonedObject.profession); // Output: 'Senior Software Developer'
const defaultSettings = {
theme: 'dark',
showNotifications: true,
sounds: true
};
const userSettings = {
theme: 'light',
sounds: false
};
const finalSettings = Object.assign({}, defaultSettings, userSettings);
console.log(finalSettings);
// Output: { theme: 'light', showNotifications: true, sounds: false }
function deepMerge(target, source) {
for (const key of Object.keys(source)) {
if (source[key] instanceof Object && key in target) {
Object.assign(source[key], deepMerge(target[key], source[key]));
}
}
Object.assign(target || {}, source);
return target;
}
const config1 = {
database: {
host: 'localhost',
port: 3306
},
server: {
port: 8000
}
};
const config2 = {
database: {
port: 5432,
username: 'admin'
},
server: {
host: '127.0.0.1'
}
};
const finalConfig = deepMerge(config1, config2);
console.log(finalConfig);
// Output:
// {
// database: { host: 'localhost', port: 5432, username: 'admin' },
// server: { port: 8000, host: '127.0.0.1' }
// }
const canEat = {
eat() {
console.log('Eating...');
}
};
const canWalk = {
walk() {
console.log('Walking...');
}
};
const person = Object.assign({}, canEat, canWalk);
person.eat(); // Output: 'Eating...'
person.walk(); // Output: 'Walking...'
These examples demonstrate practical uses of Object.assign
in various real-world scenarios, highlighting its versatility in handling object manipulation tasks in JavaScript.