-
Notifications
You must be signed in to change notification settings - Fork 5
Labels
Description
Info
difficulty: medium
title: Object.assign()
type: question
template: javascript
tags: javascriptQuestion
Implement your own version of Object.assign() that copies enumerable own properties from source objects to a target object. This is a fundamental JavaScript utility that powers the object spread operator under the hood.
Requirements
- Property copying – Copy all enumerable own properties from sources to target
- Multiple sources – Support variadic arguments with multiple source objects
- Property overwriting – Later sources should override earlier properties
- Return target – Return the modified target object (same reference)
- Edge case handling – Handle
null,undefined, and primitive sources gracefully
Key Behaviors
- Enumerable properties only – Skip non-enumerable and inherited properties
- Symbol properties – Copy symbol-keyed properties when enumerable
- Primitive sources – Ignore
null,undefined, strings, numbers, booleans - Property precedence – Later sources override earlier ones
- Reference preservation – Return the same target object reference
Example
const target = { a: 1 };
const source1 = { b: 2, c: 3 };
const source2 = { c: 4, d: 5 };
objectAssign(target, source1, source2);
// target is now { a: 1, b: 2, c: 4, d: 5 }
// Returns the same target object
objectAssign({}, { a: 1 }, null, undefined, { b: 2 });
// Returns { a: 1, b: 2 }
// Symbol properties
const symbol = Symbol('key');
objectAssign({}, { [symbol]: 'value', regular: 'prop' });
// Returns { regular: 'prop', [Symbol(key)]: 'value' }Key Challenge
The function must properly iterate over enumerable own properties while handling edge cases like primitive sources and maintaining the correct property precedence order.
Template (JavaScript)
javascript.template.md
export function objectAssign(target, ...sources) {
// TODO: Implement me
}import { objectAssign } from './index';
describe('objectAssign', () => {
it('should copy properties from source to target', () => {
const target = { a: 1 };
const source = { b: 2, c: 3 };
const result = objectAssign(target, source);
expect(result).toBe(target);
expect(result).toEqual({ a: 1, b: 2, c: 3 });
});
it('should handle multiple sources', () => {
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
const result = objectAssign(target, source1, source2);
expect(result).toEqual({ a: 1, b: 2, c: 3 });
});
it('should overwrite existing properties', () => {
const target = { a: 1, b: 2 };
const source = { b: 3, c: 4 };
const result = objectAssign(target, source);
expect(result).toEqual({ a: 1, b: 3, c: 4 });
});
it('should handle null and undefined sources', () => {
const target = { a: 1 };
const result = objectAssign(target, null, undefined, { b: 2 });
expect(result).toEqual({ a: 1, b: 2 });
});
it('should handle primitive sources', () => {
const target = { a: 1 };
const result = objectAssign(target, 'string', 42, true, null);
expect(result).toEqual({ a: 1 });
});
it('should handle empty target and sources', () => {
const result = objectAssign({}, { a: 1 }, {});
expect(result).toEqual({ a: 1 });
});
it('should not copy non-enumerable properties', () => {
const source = {};
Object.defineProperty(source, 'nonEnumerable', {
value: 'test',
enumerable: false
});
const target = {};
const result = objectAssign(target, source);
expect(result).toEqual({});
expect('nonEnumerable' in result).toBe(false);
});
it('should handle symbol properties', () => {
const symbol = Symbol('test');
const target = {};
const source = { [symbol]: 'value', regular: 'prop' };
const result = objectAssign(target, source);
expect(result).toEqual({ regular: 'prop' });
expect(result[symbol]).toBe('value');
});
});Template (TypeScript)
typescript.template.md
export function objectAssign<T extends Record<string, any>>(target: T, ...sources: any[]): T {
// TODO: Implement me
}import { objectAssign } from './index';
describe('objectAssign', () => {
it('should copy properties from source to target', () => {
const target = { a: 1 };
const source = { b: 2, c: 3 };
const result = objectAssign(target, source);
expect(result).toBe(target);
expect(result).toEqual({ a: 1, b: 2, c: 3 });
});
it('should handle multiple sources', () => {
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
const result = objectAssign(target, source1, source2);
expect(result).toEqual({ a: 1, b: 2, c: 3 });
});
it('should overwrite existing properties', () => {
const target = { a: 1, b: 2 };
const source = { b: 3, c: 4 };
const result = objectAssign(target, source);
expect(result).toEqual({ a: 1, b: 3, c: 4 });
});
it('should handle null and undefined sources', () => {
const target = { a: 1 };
const result = objectAssign(target, null, undefined, { b: 2 });
expect(result).toEqual({ a: 1, b: 2 });
});
it('should handle primitive sources', () => {
const target = { a: 1 };
const result = objectAssign(target, 'string', 42, true, null);
expect(result).toEqual({ a: 1 });
});
it('should handle empty target and sources', () => {
const result = objectAssign({}, { a: 1 }, {});
expect(result).toEqual({ a: 1 });
});
it('should not copy non-enumerable properties', () => {
const source = {};
Object.defineProperty(source, 'nonEnumerable', {
value: 'test',
enumerable: false
});
const target = {};
const result = objectAssign(target, source);
expect(result).toEqual({});
expect('nonEnumerable' in result).toBe(false);
});
it('should handle symbol properties', () => {
const symbol = Symbol('test');
const target = {};
const source = { [symbol]: 'value', regular: 'prop' };
const result = objectAssign(target, source);
expect(result).toEqual({ regular: 'prop' });
expect(result[symbol]).toBe('value');
});
});