The constructor and Properties
The constructor is the first method that runs when you create an instance. It is where you accept arguments, validate them, and assign the initial state of the object.
Assigning properties with this
Section titled “Assigning properties with this”Inside the constructor, this refers to the instance being created. Every property you want the instance to have must be assigned to this:
class Expense { constructor(id, description, amount, category, date) { this.id = id; this.description = description; this.amount = amount; this.category = category; this.date = date; }}After new Expense(1, 'Coffee', 4.50, 'Food', '2024-01-15') runs, the instance has all five properties.
Default parameter values
Section titled “Default parameter values”Constructor parameters can have defaults, just like regular function parameters. This is useful when a property is optional or has a sensible fallback:
class Expense { constructor(id, description, amount, category, date = new Date().toISOString().split('T')[0]) { this.id = id; this.description = description; this.amount = amount; this.category = category; this.date = date; }}
const expense = new Expense(1, 'Coffee', 4.50, 'Food');console.log(expense.date); // today's date in YYYY-MM-DD formatComputed properties in the constructor
Section titled “Computed properties in the constructor”You can compute additional properties inside the constructor — properties that are derived from the arguments rather than passed directly:
class Expense { constructor(id, description, amount, category, date) { this.id = id; this.description = description; this.amount = amount; this.category = category; this.date = date; this.createdAt = Date.now(); // timestamp of when the instance was created }}createdAt is not passed as an argument — it is always set to the current timestamp when the expense is created. Every instance gets it automatically.
Validating in the constructor
Section titled “Validating in the constructor”The constructor is the right place to enforce rules about what makes a valid instance. Throw an error early rather than letting invalid data silently flow through your app:
class Expense { constructor(id, description, amount, category, date) { if (typeof amount !== 'number' || amount <= 0) { throw new Error('Amount must be a positive number'); } if (!description || description.trim() === '') { throw new Error('Description is required'); }
this.id = id; this.description = description.trim(); this.amount = amount; this.category = category; this.date = date ?? new Date().toISOString().split('T')[0]; }}Now invalid data throws immediately, with a clear message, at the point where the problem was introduced — not somewhere downstream when you try to display a NaN on screen.
Accepting an options object
Section titled “Accepting an options object”For classes with many parameters, an options object is cleaner than a long positional argument list. It also makes call sites self-documenting:
class Expense { constructor({ id, description, amount, category, date }) { this.id = id; this.description = description; this.amount = amount; this.category = category; this.date = date ?? new Date().toISOString().split('T')[0]; }}
const coffee = new Expense({ id: 1, description: 'Coffee', amount: 4.50, category: 'Food',});The destructured parameter pattern { id, description, amount, category, date } is covered in depth in Module 04. For now, notice that the call site is self-documenting — you can see exactly which field is which without counting argument positions.
The final Expense constructor for BudgetBuddy
Section titled “The final Expense constructor for BudgetBuddy”Here is the constructor you will use throughout the rest of this course:
class Expense { constructor({ id, description, amount, category, date }) { if (!description || description.trim() === '') { throw new Error('Description is required'); } if (typeof amount !== 'number' || amount <= 0) { throw new Error('Amount must be a positive number'); }
this.id = id ?? crypto.randomUUID(); this.description = description.trim(); this.amount = amount; this.category = category ?? 'Uncategorized'; this.date = date ?? new Date().toISOString().split('T')[0]; this.createdAt = Date.now(); }}crypto.randomUUID() generates a unique ID automatically when none is provided — no need for a manual counter.
Exercise
Section titled “Exercise”- Build the
Expenseconstructor as shown above (with validation and defaults). - Create three valid expense instances and log their properties.
- Try creating an expense with an empty
description— confirm it throws. - Try creating an expense with a negative
amount— confirm it throws. - Create an expense without a
dateand confirm the default date is today.
thisin the constructor refers to the new instance — assign every property tothis.- Default parameter values let optional fields fall back to sensible values.
- Computed properties (like
createdAt) are set inside the constructor, not passed as arguments. - Validate in the constructor — throw early with a clear error rather than letting bad data silently propagate.
- An options object parameter is cleaner than a long positional argument list for classes with many fields.