Practice Questions
1. What is the purpose of the constructor method in a JavaScript class? Write a class called Shape with a constructor that accepts x, y, and color properties, and a method called describe that returns a string like "Shape at (10, 20) with color red".
Solution
The constructor is a special method that runs automatically when you create a new instance with new. It initializes the object’s properties.
class Shape {
constructor(x, y, color) {
this.x = x;
this.y = y;
this.color = color;
}
describe() {
return `Shape at (${this.x}, ${this.y}) with color ${this.color}`;
}
}
const s = new Shape(10, 20, "red");
console.log(s.describe()); // "Shape at (10, 20) with color red"
2. Explain what the extends keyword does in JavaScript. Then explain what super does inside a subclass constructor and why it must be called before using this.
Solution
The extends keyword creates a subclass that inherits all properties and methods from a parent class. The subclass can then add new behavior or override existing methods.
super(...) inside a subclass constructor calls the parent class’s constructor, which sets up the inherited properties. You must call super before accessing this because the parent constructor is what initializes the object. Until it runs, this does not exist yet. If you use this before super(), you get a ReferenceError.
3. Write a class Vehicle with properties make and speed, and a method accelerate(amount) that increases speed by amount. Then write a subclass ElectricVehicle that extends Vehicle, adds a battery property (initialized to 100), and overrides accelerate so that it calls the parent’s accelerate and then reduces battery by 1.
Solution
class Vehicle {
constructor(make, speed) {
this.make = make;
this.speed = speed;
}
accelerate(amount) {
this.speed += amount;
}
}
class ElectricVehicle extends Vehicle {
constructor(make, speed) {
super(make, speed);
this.battery = 100;
}
accelerate(amount) {
super.accelerate(amount);
this.battery -= 1;
}
}
const ev = new ElectricVehicle("Tesla", 0);
ev.accelerate(30);
console.log(ev.speed); // 30
console.log(ev.battery); // 99
4. What is the difference between method overriding and method overloading? Does JavaScript support both? Explain your answer.
Solution
Method overriding means a subclass defines a method with the same name as one in its parent class. When that method is called on a subclass instance, the subclass version runs instead of the parent’s.
Method overloading means having multiple versions of the same method with different parameter lists, where the correct version is chosen based on the arguments provided.
JavaScript supports method overriding but does not support method overloading. Since JavaScript allows any function to be called with any number of arguments (extra arguments are ignored, missing ones become undefined), there is only ever one version of a method per name on a given object. If a subclass defines a method with the same name as the parent, it replaces the parent’s version entirely. That is overriding, not overloading.
5. Explain what requestAnimationFrame does and why it is preferred over setInterval for animations. Write a short code sketch that uses requestAnimationFrame to repeatedly call a draw function, and show how you would stop the loop using a boolean flag.
Solution
requestAnimationFrame tells the browser to call a given function before the next screen repaint, typically around 60 times per second. It is preferred over setInterval because it syncs with the display’s refresh rate, pauses when the tab is not visible (saving resources), and produces smoother animations.
let running = true;
function draw() {
// Clear and redraw scene here...
if (running) {
window.requestAnimationFrame(draw);
}
}
draw(); // Start the loop
// To stop: set running = false
When running is set to false, the next frame does not schedule another call to requestAnimationFrame, and the loop ends.
6. When animating on a canvas, why is it necessary to call clearRect at the beginning of each frame? What visual artifact would you see if you removed it?
Solution
clearRect erases the entire canvas (or a portion of it) before drawing the next frame. Without it, each new frame is painted on top of the previous one, so every position the object has been in is still on the screen. You get a “trail” or “smear” effect, where you see the whole path the object took instead of just where it is now.
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height); // Erase previous frame
// Draw objects at their new positions...
window.requestAnimationFrame(draw);
}
7. Write a class Widget with a draw(ctx) method that draws a filled rectangle. Then write a subclass ToggleWidget that adds a visible property (defaulting to true) and overrides draw so that it only calls super.draw(ctx) when visible is true. Explain why this pattern is useful.
Solution
class Widget {
constructor(x, y, width, height, color) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.color = color;
}
draw(ctx) {
ctx.beginPath();
ctx.rect(this.x, this.y, this.width, this.height);
ctx.fillStyle = this.color;
ctx.fill();
ctx.closePath();
}
}
class ToggleWidget extends Widget {
constructor(x, y, width, height, color) {
super(x, y, width, height, color);
this.visible = true;
}
draw(ctx) {
if (this.visible) {
super.draw(ctx);
}
}
}
This pattern is useful because it lets you hide an object without removing it from your data structure. You can set visible to false to stop rendering and skip collision checks, and the object is still there in memory (e.g., for score tracking or resetting the scene). The subclass only adds the part that is different from the parent, so there is less code to write and the parent’s draw can still be reused.
8. Explain the role of .bind(this) when passing a class method as a callback to addEventListener. What would happen if you omitted it?
Solution
When you pass a method as a callback to addEventListener, the browser calls that function with this set to the element the listener is attached to (event.currentTarget), not the class instance. This means any references to instance properties like this.speed or this.dx inside the handler would be undefined or point to the wrong object.
Calling .bind(this) creates a new function where this is permanently bound to the class instance, so the handler can correctly access instance properties:
document.addEventListener("keydown", this.handleKey.bind(this));
Without .bind(this), this inside handleKey would refer to the element the listener was attached to (here, document), not the object that owns the method.
9. Given the following class hierarchy, what will the code below log and why?
class A {
greet() {
return "Hello from A";
}
}
class B extends A {
greet() {
return "Hello from B";
}
}
class C extends B {}
const obj = new C();
console.log(obj.greet());
Solution
It logs "Hello from B".
When obj.greet() is called, JavaScript looks for greet on the instance first, then walks up the prototype chain. C does not define its own greet, so the engine moves to B, which does define greet. That method runs and returns "Hello from B". The engine never gets as far as A, because it already found a greet on B. That is how method overriding works with the prototype chain.
10. Write a function detectCollision(rect1, rect2) that takes two objects, each with x, y, width, and height properties, and returns true if the rectangles overlap (axis-aligned bounding box intersection). Explain the logic behind your condition.
Solution
function detectCollision(rect1, rect2) {
return (
rect1.x < rect2.x + rect2.width &&
rect1.x + rect1.width > rect2.x &&
rect1.y < rect2.y + rect2.height &&
rect1.y + rect1.height > rect2.y
);
}
The logic checks that no gap exists between the two rectangles on either axis. Two rectangles overlap if and only if:
rect1’s left edge is to the left ofrect2’s right edge, ANDrect1’s right edge is to the right ofrect2’s left edge, ANDrect1’s top edge is aboverect2’s bottom edge, ANDrect1’s bottom edge is belowrect2’s top edge.
If any one of these conditions fails, there is a gap on that axis and the rectangles do not intersect. This is known as Axis-Aligned Bounding Box (AABB) collision detection.
11. Why is it important to keep responsibilities separated in your code – for example, having a bounce method return a boolean signal rather than directly calling alert or stopping an animation loop? What design benefit does this provide?
Solution
Keeping responsibilities separated means each piece of code does one job and reports its result, without assuming what the caller will do with it. When bounce returns a boolean (e.g., true if the ball is still in play, false if it fell off the screen), the caller decides what to do with that information: show an alert, stop the loop, update a score, or anything else.
This provides several benefits:
- Reusability: The
bouncemethod can be used in different contexts without modification. - Testability: You can test
bouncein isolation by checking its return value, without needing to mockalertorrequestAnimationFrame. - Flexibility: If you later want to add lives or a restart feature, you only change the caller, not the
bouncemethod itself.
This principle is called “separation of concerns.”
12. Describe the Canvas coordinate system. Where is the origin, and which direction do the x and y axes increase? Write code that obtains a 2D rendering context and draws a filled blue rectangle at position (50, 100) with width 200 and height 30.
Solution
The Canvas coordinate system has its origin (0, 0) at the top-left corner. The x-axis increases to the right, and the y-axis increases downward (opposite to the typical math convention).
const canvas = document.getElementById("game");
const ctx = canvas.getContext("2d");
ctx.fillStyle = "blue";
ctx.fillRect(50, 100, 200, 30);
getContext("2d")returns the 2D rendering context, which provides all the drawing methods.fillStylesets the color for subsequent fill operations.fillRect(x, y, width, height)draws a filled rectangle at the specified position and size.
The downward y-axis means that y = 0 is the top of the canvas, and increasing y moves the drawing lower on the screen. Keep that in mind for positioning and collision math. For example, “moving up” means decreasing y.