Exercises
Objectives
This chapter introduces no new concepts. It’s a chance to practice everything from this module: the this keyword, classes, constructors, static methods, encapsulation, private fields, inheritance, and polymorphism.
Exercises
-
Write an object
{ name: "Jordan", greet() { returnHi, I’m ${this.name}; } }. Call.greet()normally and log it, then assign the method to a separate variable (const g = obj.greet) and callg()inside atry/catch. Check: the direct call logsHi, I'm Jordan, and the extracted call throws aTypeErrorbecausethisisundefined. -
Write a plain function
formatPrice(currency)that returns`${currency}${this.amount}`, and an object{ amount: 25 }. CallformatPricethree ways against that object: with.call(), with.apply(), and by creating a bound version with.bind()first. Check: all three produce the same result,$25if you pass"$"as the currency. -
Write a class
Bookwithconstructor(title, author, pages)that stores all three as properties. Add an instance methodsummary()that returns`${title} by ${author} (${pages} pages)`. Createnew Book("Dune", "Frank Herbert", 412)and log.summary(). Check: the output must read exactlyDune by Frank Herbert (412 pages). -
Add a fourth constructor parameter to
Bookcalledavailablethat defaults totrueif not passed. Create one book without anavailableargument and one passingfalseexplicitly. Check: logging.availableon each givestrueandfalse. -
Add a static method
Book.blank()that returnsnew Book("Untitled", "Unknown", 0). Check:Book.blank().summary()logsUntitled by Unknown (0 pages). -
Write a class
MathHelperwith a static methodsquare(n)that returnsn * n, and no instance methods. Check:MathHelper.square(5)returns25. Then check thatnew MathHelper().squareisundefined, since static methods aren’t reachable from instances. -
Write a class
Walletwith a private field#cashstarting at0, a methodaddCash(amount)that increases it, a methodspend(amount)that decreases it and returns the new total, or returns the string"Not enough cash"ifamountis greater than the current balance, and a methodgetCash()that returns the current value. Create a wallet, calladdCash(50), thenspend(20). Check:getCash()returns30. -
Using the
Walletclass from exercise 7, try writingmyWallet.#cashdirectly in your file, outside the class body. Run the file withnode. Check: instead of returning a value, it throwsSyntaxError: Private field '#cash' must be declared in an enclosing class. -
Write a class
Playlistwith a private field#songs(an array), a methodaddSong(title)that pushes onto it, and a methodgetSongs()that returns a copy of the array, for example with[...this.#songs], not the original array. Check: calling.push()on the array returned bygetSongs()does not change what a second call togetSongs()returns. -
Write a base class
Employeewithconstructor(name, salary)and a methoddescribe()that returns`${name} earns ${salary}`. Write a subclassManager extends Employeewithconstructor(name, salary, teamSize)that callssuper(name, salary)and storesteamSize. Do not overridedescribe()yet. Check:new Manager("Sam", 70000, 4).teamSizeis4, and.describe()still works even thoughManagernever defined it. -
Override
describe()inManagerso it callssuper.describe()and appends`, managing ${teamSize} people`to the result. Check:new Manager("Sam", 70000, 4).describe()returns exactlySam earns 70000, managing 4 people. -
Write a second subclass
Intern extends Employeethat overridesdescribe()to return`${name} is interning`. Put oneEmployee, oneManager, and oneInternin an array and loop over it, calling.describe()on each. Check: the three lines of output are all different, each matching its own class’s version ofdescribe(). -
Add a static method
Employee.payFrequency()that returns"monthly". Do not add anything toManagerorIntern. Check:Manager.payFrequency()andIntern.payFrequency()both return"monthly", proving static methods are inherited too. -
Using the classes from exercise 12, create
const erin = new Intern("Erin", 0). Check:erin instanceof Internanderin instanceof Employeeare bothtrue, anderin instanceof Managerisfalse. -
Write a class
Accountwith a private field#balance, aconstructor(balance), adeposit(amount)method, and agetBalance()method, all touching#balancethe way lesson 4 showed. Write a subclassSavingsAccount extends Accountwithconstructor(balance, rate)that callssuper(balance)and storesrate, plus a methodapplyInterest()that computesthis.getBalance() * this.rateand passes it tothis.deposit(...). Check:new SavingsAccount(100, 0.1).applyInterest()returns110. -
Write a static factory method
Shape.create(type, size)on a base classShape(with subclassesCircleandSquare, each with adescribe()override) that returnsnew Circle(size)whentype === "circle"andnew Square(size)otherwise. Check:Shape.create("circle", 4).describe()andShape.create("square", 4).describe()produce two different, class-specific strings.
Recap
You can now define classes with constructors and static methods, protect internal state with private fields, build class hierarchies with extends and super, and write polymorphic code that treats different subclasses the same way while each still behaves like itself.
Next module: error handling, catching problems with try/catch/finally and raising your own errors with throw.