Classes in JavaScript


JavaScript is an object oriented programming language. Almost everything in JS is an object, with its attributes and methods.

Class creates a user-defined data structure, which holds its own data members (attributes) and member functions (methods), which can be accessed and used by creating an instance of that class.

Classes are like a blueprint for objects outlining possible behaviors and states that every object of a certain type could have.

Classes are defined via the keyword class. We should always add a method named constructor(), we'll see why later.

// Define an empty class Employee
class Employee {
	constructor() {}
};

Objects/Instances

An Object is an instance of a Class. A class is like a blueprint while an instance is a copy of the class with actual values. It’s not an idea anymore, it’s an actual employee, like a person named "Andrew" who is 30 years old.

You can have many employees to create many different instances, but without the class as a guide, you would be lost, not knowing what information is required.

After a class is defined, we can create objects of the class using the new keyword like this:

// Define an empty class Employee
class Employee {
	constructor() {}
};

let employee = new Employee();

Attributes/Properties

A class attribute is a JavaScript variable that belongs to a class and is shared between all the objects of this class. Let's add some attributes to our Employee class:

class Employee {
	constructor(name, age) {
		this.name = name;
		this.age = age;
	}
};

let emp = new Employee('Andrew', 30);
	
console.log(emp.name); // prints 'Andrew'
	
emp.age = 40;

console.log(emp.age); // prints 40

We added two properties: the string name and the number age.

Now you can see why constructor() is so important: it is a special method for creating and initializing the attributes within an object.

The attributes of objects can be accessed and modified using the dot operator (.), like we did above with emp.name and emp.age.

The "this"

Every object in JavaScript has access to its own address through an important refference called this.

The this refference is an implicit parameter to all member functions. Therefore, inside a member function, this may be used to access and manipulate the attributes of that object, like we did above with this.name = name.

Member functions

Member functions are functions that belong to the class:

class Employee {
	constructor(name, age) {
		this.name = name;
		this.age = age;
	}
	
	// member functions:
	printName() {
		console.log(this.name);
	}
		
	printEmployee() {
		this.printName();
		console.log(this.age);
	}
};

let emp1 = new Employee('Andrew', 30);
	
emp1.printEmployee(); // prints 'Andrew', 30

let emp2 = new Employee('Mary', 25);
	
emp2.printEmployee(); // prints 'Mary', 25

As you can see, we access functions just like we access attributes: using the dot syntax (.).

The function call uses the attributes of the object it was called on. That's why the first printEmployee() prints 'Andrew' and 30 and the second prints 'Mary' and 25.

One more important thing to know is that when we call a member function from an other member function, we should use the this keyword like we did above: this.printName();

Rewriting classes as pure methods

Throught the tutorials you will notice that we don't use the class syntax. This is because a class can be also written as a function:

function Employee(name, age) {
	this.name = name;
	this.age = age;
	this.printName = function() {
		console.log(this.name);
	};
	this.printEmployee = function() {
		this.printName();
		console.log(this.age);
	};
}

let emp = new Employee('Andrew', 30);
emp.printEmployee();


Assignment
Follow the Coding Tutorial and let's write some classes.


Hint
Look at the examples above if you get stuck.


Introduction

In this lesson, we will explore the concept of classes in JavaScript. Classes are a fundamental part of object-oriented programming (OOP) and are used to create objects that share common properties and methods. Understanding classes is crucial for writing clean, efficient, and maintainable code in JavaScript.

Classes are particularly useful in scenarios where you need to create multiple objects with similar properties and methods, such as creating multiple instances of a user, product, or employee in an application.

Understanding the Basics

Before diving into the details, let's understand the basic concepts related to classes in JavaScript:

Let's start with a simple example to illustrate these concepts:

class Employee {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}

let emp = new Employee('Andrew', 30);
console.log(emp.name); // prints 'Andrew'
console.log(emp.age); // prints 30

Main Concepts

Now that we have a basic understanding, let's delve deeper into the key concepts and techniques involved in using classes in JavaScript:

Defining a Class

Classes are defined using the class keyword followed by the class name. The constructor method is used to initialize the properties of the class:

class Employee {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}

Creating Instances

Instances of a class are created using the new keyword:

let emp = new Employee('Andrew', 30);

Accessing Properties and Methods

Properties and methods of an instance can be accessed using the dot operator:

console.log(emp.name); // prints 'Andrew'
emp.age = 40;
console.log(emp.age); // prints 40

Examples and Use Cases

Let's look at some examples to see how classes can be used in various contexts:

Example 1: Employee Class

class Employee {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  printName() {
    console.log(this.name);
  }

  printEmployee() {
    this.printName();
    console.log(this.age);
  }
}

let emp1 = new Employee('Andrew', 30);
emp1.printEmployee(); // prints 'Andrew', 30

let emp2 = new Employee('Mary', 25);
emp2.printEmployee(); // prints 'Mary', 25

Example 2: Product Class

class Product {
  constructor(name, price) {
    this.name = name;
    this.price = price;
  }

  printProduct() {
    console.log(`Product: ${this.name}, Price: $${this.price}`);
  }
}

let product1 = new Product('Laptop', 1200);
product1.printProduct(); // prints 'Product: Laptop, Price: $1200'

let product2 = new Product('Phone', 800);
product2.printProduct(); // prints 'Product: Phone, Price: $800'

Common Pitfalls and Best Practices

When working with classes in JavaScript, it's important to be aware of common pitfalls and follow best practices:

Advanced Techniques

Once you are comfortable with the basics, you can explore advanced techniques such as inheritance and static methods:

Inheritance

Inheritance allows you to create a new class that extends an existing class, inheriting its properties and methods:

class Manager extends Employee {
  constructor(name, age, department) {
    super(name, age);
    this.department = department;
  }

  printManager() {
    this.printEmployee();
    console.log(`Department: ${this.department}`);
  }
}

let manager = new Manager('Alice', 35, 'HR');
manager.printManager(); // prints 'Alice', 35, 'Department: HR'

Static Methods

Static methods belong to the class itself rather than instances of the class:

class Utility {
  static printMessage(message) {
    console.log(message);
  }
}

Utility.printMessage('Hello, World!'); // prints 'Hello, World!'

Code Implementation

Here is a complete example demonstrating the use of classes, inheritance, and static methods:

class Employee {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  printName() {
    console.log(this.name);
  }

  printEmployee() {
    this.printName();
    console.log(this.age);
  }
}

class Manager extends Employee {
  constructor(name, age, department) {
    super(name, age);
    this.department = department;
  }

  printManager() {
    this.printEmployee();
    console.log(`Department: ${this.department}`);
  }
}

class Utility {
  static printMessage(message) {
    console.log(message);
  }
}

let emp = new Employee('Andrew', 30);
emp.printEmployee(); // prints 'Andrew', 30

let manager = new Manager('Alice', 35, 'HR');
manager.printManager(); // prints 'Alice', 35, 'Department: HR'

Utility.printMessage('Hello, World!'); // prints 'Hello, World!'

Debugging and Testing

Debugging and testing are crucial for ensuring the correctness of your code. Here are some tips:

Example of a simple test case using Jest:

const { Employee } = require('./Employee');

test('Employee class should initialize with name and age', () => {
  let emp = new Employee('Andrew', 30);
  expect(emp.name).toBe('Andrew');
  expect(emp.age).toBe(30);
});

Thinking and Problem-Solving Tips

When approaching problems related to classes, consider the following strategies:

Conclusion

In this lesson, we covered the basics of classes in JavaScript, including how to define classes, create instances, and use properties and methods. We also explored advanced techniques like inheritance and static methods. Understanding and mastering classes is essential for writing efficient and maintainable code in JavaScript.

Keep practicing and exploring further applications of classes to deepen your understanding and improve your coding skills.

Additional Resources

For further reading and practice, check out the following resources: