Skip to content

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.

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.

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 format

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.

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.

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.

  1. Build the Expense constructor as shown above (with validation and defaults).
  2. Create three valid expense instances and log their properties.
  3. Try creating an expense with an empty description — confirm it throws.
  4. Try creating an expense with a negative amount — confirm it throws.
  5. Create an expense without a date and confirm the default date is today.
  • this in the constructor refers to the new instance — assign every property to this.
  • 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.