Problem overview
A company pays every employee a salary. A manager is an employee too, but earns a bonus on top. The task is to write two classes so that Manager reuses everything Employee has and changes only how its pay is worked out.
Employee(name, salary)keeps the name and the salary in private fields.pay()returns the salary.describe()returns the text<name> earns <pay>, such asAna earns 3000.Manager(name, salary, bonus)extendsEmployee. The parent's constructor stores the name and the salary, and the manager stores the bonus in its own private field. Itspay()returns the parent's pay plus the bonus. It does not writedescribe()again.
An employee Ana with salary 3000 describes herself as Ana earns 3000. A manager Bob with salary 4000 and bonus 1000 describes himself as Bob earns 5000, even though describe() is Employee's code.
Approach
describe() must not read the salary field directly. It calls this.pay(). When the object is an Employee, that runs Employee's pay. When the object is a Manager, this.pay() finds the manager's own version first, so the bonus shows up without any second describe. One method written once works for both classes.
The private fields shape the rest. A #salary declared in Employee can be used only by code inside Employee, not by Manager. So Manager gets the salary the only way it can: by calling the parent's pay() with super.pay().
Step by step
- Private fields of Employee. Declare
#nameand#salaryinside the class body. - Constructor. Assign the parameters to
this.#nameandthis.#salary, so each object has its own values. - pay(). For an employee, return
this.#salary. - describe(). Return the name, then
" earns ", thenthis.pay(), joined with+. A number added to a string with+becomes text. Writethis.pay(), notthis.#salary. - Manager. Write
class Manager extends Employee, and declare its own private#bonus. - Manager's constructor. Call
super(name, salary)first. It runs the parent's constructor, which fills in#nameand#salary. In a derived class, JavaScript requires this call before the constructor usesthis. - Store the bonus in
this.#bonus, aftersuper(...). - Manager's pay(). Return
super.pay() + this.#bonus.
The solution in JavaScript
class Employee {
#name;
#salary;
constructor(name, salary) {
this.#name = name;
this.#salary = salary;
}
pay() {
return this.#salary;
}
describe() {
return this.#name + " earns " + this.pay();
}
}
class Manager extends Employee {
#bonus;
constructor(name, salary, bonus) {
super(name, salary);
this.#bonus = bonus;
}
pay() {
return super.pay() + this.#bonus;
}
}
Using the classes
const people = [
new Employee("Ana", 3000),
new Manager("Bob", 4000, 1000),
new Manager("Cleo", 5000, 0),
new Employee("Dan", 5000),
new Manager("Eve", 7000, 2500),
new Employee("Finn", 2800),
new Manager("Hana", 6000, 500),
];
for (const p of people) {
console.log(p.describe());
}
This prints:
Ana earns 3000
Bob earns 5000
Cleo earns 5000
Dan earns 5000
Eve earns 9500
Finn earns 2800
Hana earns 6500
Cleo's bonus is 0, so she earns exactly her salary, like Dan. For a manager, pay() returns the salary plus the bonus, and m instanceof Employee is true: a manager is an employee.
Why super.pay()
Inside Manager.pay, this.pay() would be the same method calling itself, again and again until JavaScript stops it with RangeError: Maximum call stack size exceeded. super.pay() asks for the parent's version, which returns the salary. It is also the right call for a second reason: the salary belongs to Employee, and Employee's pay() is the code that knows how an employee is paid. If that rule changes, Manager follows automatically.
Common mistakes
- Using
thisbeforesuper(...). In a derived class, touchingthisfirst fails withReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor. - Reading
this.#salaryin Manager. A private field can be used only inside the class that declares it, so the class body of Manager can't use#salary. Using it there is aSyntaxError, because the name is not declared in an enclosing class. - Calling
this.pay()inside Manager'spay. This is the endless recursion described above. - Repeating
describe()in Manager. It isn't needed. Becausedescribecallsthis.pay(), the inherited method already uses the manager's pay. - Skipping the parent's constructor. Without
super(name, salary), the parent's fields are never set.
Testing
A few inputs worth trying:
- The largest values in the constraints:
new Manager("Zed", 1000000, 1000000).describe()returnsZed earns 2000000. - A salary of 0:
new Employee("Zed", 0).describe()returnsZed earns 0. - Two managers with the same name:
new Manager("Ann", 100, 50)describes itself asAnn earns 150andnew Manager("Ann", 200, 5)asAnn earns 205. Each object keeps its own numbers.
What inheritance gives you here
Manager has no describe method of its own. When describe() is called on a manager, JavaScript looks in Manager first, finds nothing, and continues to Employee, where it runs the method with this still pointing at the manager. That is why one definition serves both classes, and why the inner call this.pay() reaches Manager's pay.
The private fields work the same way as in any class. #name and #salary exist on every manager, because the parent's constructor created them, but only code written inside Employee can use those names. The text of Manager can't, and that restriction is what pushes the design toward super.pay().
Another way to build the text
A template literal builds the same text without +:
describe() {
return `${this.#name} earns ${this.pay()}`;
}
With this version, new Employee("Ana", 3000).describe() returns Ana earns 3000 and new Manager("Bob", 4000, 1000).describe() returns Bob earns 5000, exactly like the solution above. Either way, the amount comes from this.pay(), never from a field, so the manager's bonus is included.
When to inherit
Inheritance fits when one class really is a kind of another: a manager is an employee, so it can be used anywhere an employee is expected, and it adds or changes only what is different. Here the difference is a single method and a single field. If the two classes shared nothing but a few lines of code, a helper function would be a better tool than a parent class.
