Skip to Content
Lab 2

Classes and Inheritance

Overview and Objectives

This lab introduces the modern JavaScript class syntax and demonstrates how to define classes, use getters/setters, and implement inheritance using the extends keyword. Learners build a base class and a subclass, test static properties, and explore method overriding to understand object-oriented programming in JavaScript. By the end of this lab, learners will be able to:

  • Declare classes in JavaScript using the class syntax with constructors to initialize object state.
  • Define instance properties and methods within a class.
  • Implement getters and setters to control access to object properties.
  • Create subclasses using the extends keyword to establish inheritance.
  • Utilize the super constructor call to initialize base class state in a subclass.
  • Define and use static properties or methods that belong to the class itself.
  • Understand the relationship between classes and traditional constructor functions/prototypes.

Tasks and Instructions

You will create a small class hierarchy to model a product in an inventory and demonstrate class features like inheritance, getters/setters, and static members.

Task 1: Define a Base Class

  • Create a class Product. Give it a constructor that accepts at least two parameters, for example name and price, and initializes those as properties of the instance (this.name, this.price).
    • Inside the class, define a method describe() that returns a string describing the product, e.g., "<name> costs $<price>". (Use this.name and this.price in the string.)
    • Also, add a getter for the price property that simply returns the price, and a setter for price that ensures the new price is not negative (if a negative value is assigned, perhaps set it to 0 or throw an error). This will provide controlled access to the price property.
    • Test: After defining the class, create an instance of Product (for example, const prod1 = new Product("Laptop", 1200)). Use the describe() method and the price getter to verify they work (e.g., log prod1.describe() and prod1.price). Then try using the price setter (e.g., prod1.price = -50) and ensure it either prevents the change or handles it as you designed (e.g., sets to 0). Log the result to confirm the setter’s behavior.

Task 2: Static Members

  • Add a static property count to the Product class (using the static keyword) and initialize it to 0. Each time the constructor is called (i.e., a new product is created), increment this Product.count by 1. This will track how many products have been created.
    • Also add a static method (e.g., printCount()) that logs or returns the total count of products.
    • Test: After creating a few Product instances (e.g., create prod2 = new Product("Phone", 800) etc.), call Product.printCount() (or access Product.count) to verify that the count has incremented correctly and reflects the number of instances.

Task 3: Create a Subclass

  • Define a class Book that extends Product (use class Book extends Product). The Book class should have a constructor that takes at least the same parameters as Product (name and price) plus an additional one specific to books (for example, author).
    • In the Book constructor, call super(name, price) to initialize the inherited properties, then set the additional property (e.g., this.author = author).
    • Override the describe() method in Book to include the author’s name in the description. For example, it could return a string like "<name> by <author> costs $<price>". Use super.describe() as a starting point or build a new string including this.author.
    • Test: Create an instance of Book (e.g., const book1 = new Book("1984", 15, "George Orwell")). Call book1.describe() and ensure it includes the book’s name, price, and author. Also test that inherited features work: for instance, use the inherited price getter or setter (book1.price) and verify that Book.count (the static count) also reflects this new instance. Ensure that calling Book.printCount() works and includes books in the count (because they are also Products).

Task 4: Using Class Instances

  • Interact with your objects to demonstrate the class features. For example:
    • Change the price of the Book instance using the price setter (e.g., book1.price = 20) and then get it via the getter to ensure the setter logic was applied (no negative prices, etc.).
    • Call the describe() method on both a Product instance and the Book instance to show the different outputs.
    • If you implemented another method or property, demonstrate it as well (for instance, if you added a method specific to Book).
    • Also, try referencing the static count via the subclass (e.g., Book.count should also reflect the total count, since static properties are inherited). You could call Book.printCount() to confirm it works from the subclass perspective too.

Task 5: Classes vs Constructor Functions (conceptual)

  • (No code needed for this part, but think about it or discuss in comments) Reflect on how the class syntax you used relates to the older way of creating objects using constructor functions and prototypes. For example, consider that the Product class could be implemented with a function Product(name, price) and adding methods to Product.prototype. You do not need to write this out unless instructed, but understanding this connection solidifies that JavaScript classes are built on top of prototypes.

Submission Criteria

Via a GitHub repository, submit the following via Canvas:

  • Submit the JavaScript code file lab2.js containing your class definitions and the test code that creates instances and demonstrates each requirement. Ensure your submission shows the outputs (via console.log or similar) for the tests described, so that each feature (constructor, method, getter/setter, static, inheritance) can be verified.
  • The code should run without errors in Node.js or a browser console. You should be able to create instances and call the class methods as described. If any part of a requirement is not implemented, include comments explaining what you expected to do, to possibly earn partial credit.
  • Ensure that your class and subclass names are exactly as requested and that static members and methods can be accessed as described (on the class, not instances).

Grading Rubric

CriteriaPoints
Product class definition: Class Product is correctly declared with a constructor that initializes name and price.8 points
Product methods and properties: describe() method in Product works as specified, and getter/setter for price correctly control access (e.g., preventing negative values).8 points
Static member usage: A static property (e.g., count) is implemented and updated in the constructor, and a static method (or direct property access) correctly reports the count of instances.6 points
Book subclass definition: Class Book extends Product correctly, with a constructor that calls super() and adds an author property.7 points
Method overriding: The Book class overrides describe() (or adds a new method) to include book-specific information, and this method works correctly.7 points
Inheritance behavior: Instances of Book inherit properties and methods from Product (e.g., price getter/setter, static count). Demonstrated by using Book instances and verifying they have expected functionality from both classes.6 points
Testing and output: The submitted code creates instances of both classes and demonstrates all functionalities (method outputs, getter/setter, static count) via console logs or comments. The code runs without errors.8 points

Total Points: 50 points

Note

Partial credit is available. Even if not all class features are implemented, you will earn points for each correctly implemented part (as outlined in the rubric). For instance, if the base class works but the subclass has issues, you will still get credit for the base class implementation.