Interview question
What is composition and how does it compare to inheritance in JavaScript? Composition क्या है और JavaScript में inheritance से कैसे compare होता है?
Answer
Composition builds objects by combining smaller, focused pieces of functionality rather than through a class hierarchy. It's often favored over deep inheritance chains because it's more flexible.
| Aspect | Inheritance | Composition |
|---|---|---|
| Relationship | 'is-a' (Dog is an Animal) | 'has-a' (Car has an Engine) |
| Flexibility | Rigid, fixed at class definition | Flexible, can mix and match behaviors |
| Coupling | Tightly coupled to parent class | Loosely coupled, independent pieces |
// INHERITANCE approach - can lead to rigid hierarchies
class Bird {
fly() { return 'Flying'; }
}
class Penguin extends Bird {
// Problem: penguins can't actually fly, but they inherit fly() anyway
fly() { throw new Error('Penguins cannot fly!'); } // awkward override
}
// COMPOSITION approach - combine only the behaviors that apply
const canFly = (state) => ({
fly: () => `${state.name} is flying`
});
const canSwim = (state) => ({
swim: () => `${state.name} is swimming`
});
const canWalk = (state) => ({
walk: () => `${state.name} is walking`
});
function createBird(name, abilities) {
const state = { name };
return Object.assign({}, state, ...abilities.map(ability => ability(state)));
}
const eagle = createBird('Eagle', [canFly, canWalk]);
console.log(eagle.fly()); // Eagle is flying
console.log(eagle.walk()); // Eagle is walking
const penguin = createBird('Penguin', [canSwim, canWalk]); // no canFly - accurate!
console.log(penguin.swim()); // Penguin is swimming
// penguin.fly(); // TypeError - fly is not a function, correctly doesn't exist
// Composition using classes and dependency injection
class Engine {
start() { return 'Engine starting'; }
}
class Car {
constructor(engine) {
this.engine = engine; // 'has-a' relationship, composed not inherited
}
start() {
return this.engine.start();
}
}
const car = new Car(new Engine());
console.log(car.start()); // Engine starting
// The engine can easily be swapped for a different implementation (e.g. ElectricEngine)| पहलू | Inheritance | Composition |
|---|---|---|
| Relationship | 'is-a' (Dog is Animal) | 'has-a' (Car has Engine) |
| Flexibility | Rigid | Flexible |
| Coupling | Parent से tightly coupled | Loosely coupled |
// INHERITANCE approach - rigid hierarchies बन सकती हैं
class Bird {
fly() { return 'Flying'; }
}
class Penguin extends Bird {
fly() { throw new Error('Penguins cannot fly!'); } // awkward
}
// COMPOSITION approach - सिर्फ applicable behaviors combine करना
const canFly = (state) => ({
fly: () => `${state.name} is flying`
});
const canSwim = (state) => ({
swim: () => `${state.name} is swimming`
});
const canWalk = (state) => ({
walk: () => `${state.name} is walking`
});
function createBird(name, abilities) {
const state = { name };
return Object.assign({}, state, ...abilities.map(ability => ability(state)));
}
const eagle = createBird('Eagle', [canFly, canWalk]);
console.log(eagle.fly()); // Eagle is flying
const penguin = createBird('Penguin', [canSwim, canWalk]); // canFly नहीं - सही!
console.log(penguin.swim()); // Penguin is swimming
// Dependency injection से composition
class Engine {
start() { return 'Engine starting'; }
}
class Car {
constructor(engine) {
this.engine = engine; // 'has-a' relationship
}
start() {
return this.engine.start();
}
}
const car = new Car(new Engine());
console.log(car.start()); // Engine startingWas this answer clear?