Consider a class Service that depends on Repository. The dependency is injected via the constructor. What will be the output when calling service.getData()?
class Repository { getData() { return 'data from repo'; } } class Service { constructor(repo) { this.repo = repo; } getData() { return this.repo.getData(); } } const repo = new Repository(); const service = new Service(repo); console.log(service.getData());
Think about how the Service class uses the injected Repository instance.
The Service class receives a Repository instance via its constructor and calls its getData() method. Since repo is a valid instance, the output is "data from repo".
Given a simple dependency injection container, which option correctly registers a singleton instance of Logger so that all requests get the same instance?
class Logger {} class Container { constructor() { this.services = new Map(); } registerSingleton(name, factory) { const instance = factory(); this.services.set(name, () => instance); } resolve(name) { return this.services.get(name)(); } } const container = new Container();
The registerSingleton method expects a factory function that returns an instance.
Option D passes a function that creates a new Logger instance. The container calls it once and stores the instance. Other options either pass the class itself, a function returning the class, or an instance directly, which do not match the expected factory function pattern.
Two classes A and B depend on each other via constructor injection. What error will occur when trying to instantiate A through the container?
class A { constructor(b) { this.b = b; } } class B { constructor(a) { this.a = a; } } class Container { constructor() { this.services = new Map(); } register(name, factory) { this.services.set(name, factory); } resolve(name) { const factory = this.services.get(name); return factory(this.resolve(name === 'A' ? 'B' : 'A')); } } const container = new Container(); container.register('A', (b) => new A(b)); container.register('B', (a) => new B(a)); container.resolve('A');
Think about what happens when resolve calls itself recursively without a stop condition.
The resolve method calls itself infinitely because A depends on B and B depends on A. This causes a stack overflow error.
Given a container that registers transient services (new instance every resolve), what will be the output of comparing two resolved instances?
class Service {} class Container { constructor() { this.factories = new Map(); } registerTransient(name, factory) { this.factories.set(name, factory); } resolve(name) { return this.factories.get(name)(); } } const container = new Container(); container.registerTransient('service', () => new Service()); const s1 = container.resolve('service'); const s2 = container.resolve('service'); console.log(s1 === s2);
Transient services create a new instance each time they are resolved.
Since registerTransient creates a new instance on each resolve, s1 and s2 are different objects, so the comparison is false.
Why do developers use dependency injection frameworks in large applications?
Think about how dependencies are managed and how that affects code structure and testing.
Dependency injection frameworks help by managing dependencies outside components, making code easier to test and change. They do not automatically optimize performance, enforce runtime type checks, or eliminate all manual configuration.