ES2015 introduced a new syntax into the JavaScript specification that finally allows us to create classes in a much cleaner way than the previous prototype based functional approach. Just for comparison, here is the older way of defining a class in JavaScript:
function Animal(name, species)
{
this.name = name;
this.species = species;
}
let cat = new Animal('steve', 'cat');
And here is the more syntactically accurate way to declare that exact same class using the newest specifications, though be it the exact same mechanism is used internally.
class Animal
{
constructor(name, species) {
this.name = name;
this.species = species;
}
}
let dog = new Animal('bruce', 'dog');
But from a programmer's perspective, it is definitely nice to finally be able to scan through the code and have a more immediate and direct knowing on what exactly is happening.
In the past, you essentially had to determine whether a function in JavaScript was being as a class or whether it was in fact just a function. Because at the end of the day, a class is just a collection of values and methods if we are to be honest. And the inherent meaning comes down to whatever it is that you are trying to accomplish.
Class declaration
As you saw in the example above, we can now declare a class using the class identifier followed by the class name. But you also have the option of declaring an unnamed class as follows:
let Animal = class {
constructor(name, species) {
this.name = name;
this.species = species;
}
};
console.log(Animal.name);
Using named classes will give you the benefit of being to create instances of the class on the fly throughout your code, however.
Constructor
The constructor syntax is defined using the constructor method name along with a list of parameterized values that will be used to set the various class properties. The one thing to note, is that you cannot use the keyword more than once per class declaration. If you wanted to define multiple types of constructors, then you are left with the option of using optional parameters in JavaScript.
Hoisting
In programming terms, hoisting refers to a feature that some programming languages have in which they automatically push variable declarations to the top of the page before any translation or compilation is performed. This ensures that whatever variables you have declared are indeed available to any of the code throughout the page.
The true is not the same for class declarations, however. In JavaScript, classes must be defined first before they can be instantiated. The following would produce an error for example.
let cow = new Animal('henry', 'cow');
class Animal
{
constructor(name, species){
this.name = name;
this.species = species;
}
}
Getter functions
Getter functions are common in OOP languages and are mainly used for returning processed data back from a class object. What do I mean by processed exactly? It could be any type of calculation or display output method used on a property that you don't necessarily want updating the actual values. Take the following class declaration for example:
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
// Getter
get area() {
return this.calcArea();
}
// Method
calcArea() {
return this.height * this.width;
}
}
In the example above, the area() getter function would call the calcArea() method to calculate the Rectangles calculated area and would return the value back to the caller. It won't modify any data on the class and is mainly just used to retrieve values in this case. Which is kind of how you want to think about getter() methods.
Methods vs static methods
There are 2 types of methods that you can define inside of a typical JavaScript class.
Typical methods
Standard methods are defined just as you would define a regular function in JavaScript, with a slight shorthand twist. We can define class functions without the function keyword:
let cow = new Animal('henry', 'cow');
class Animal
{
constructor(name, species){
this.name = name;
this.species = species;
}
// class method
sayName()
{
console.log(this.name);
}
}
To reference any class property inside of the declared method, you will need to prefix the variable with the this keyword.
Static methods
Static methods are declared using the static keyword before the method name. They do not belong to any instant of a class and are called using the name of the class as the prefix.
class Animal
{
constructor(name, species) {
this.name = name;
this.species = species;
}
static doSomething(){
// cool code goes here
}
}
let dog = new Animal('bruce', 'dog');
dog.doSomething() // this would be an error
Animal.doSomething() // this is correct
Static methods are mainly used as utility methods and can perform functions that are not related to the class instants data, but that might still be related to the overall inherent meaning of the class.
Instance properties
Instance properties are values that belong to every instant of a class and in JavaScript they must be defined inside of class methods. An example would be the constructor method. These values belong to each object, whether they are set or not.
constructor(name, species) {
this.name = name;
this.species = species;
}
So far JavaScript does not have a formal way of adding additional instance properties without the bulky process of including them into the constructor. There is a recommendation currently in the works for class field declarations, which you can read more about down below, but as of now they do not work in all browsers such as Firefox or Safari.
It is not impossible however to include additional properties in a class and not have them defined in the constructor method. You will just have to go the traditional route of defining them outside of the class declaration as prototype properties as such:
class Animal
{
constructor(name, species) {
this.name = name;
this.species = species;
}
}
Animal.age = 20; // belongs to the class itself
Animal.prototype.prototypeAge = 25; // belongs to every instance of this class
This will still work, however it is indeed a bulkier process and will undoubtedly be replaced by the more formal field declaration explained down below.
Field Declarations
Field declarations are not an official specification just yet and so browser compatibility is spotty at best. But they represent a more 'truer' way (if there were such a thing) of declaring properties in classes. At least a more traditional route, like with other languages such as C++ and C#. Using field declarations, the following would be equivalent to using the prototype based approach on top.
class Animal
{
age = 0; // field property with a default value
constructor(name, species) {
this.name = name;
this.species = species;
}
}
let cow = new Animal('bruce', 'cow');
cow.age = 20;
This will currently work in certain browsers, such as Chrome. So if you are interested in experimenting with the concept, that would be your best bet. Otherwise, stick to prototypes for now.
Private fields
Similar to the public field declarations defined above, the new specification also allows you to define private properties. These properties can only be accessed from within the class definition body itself and not outside.
class Animal
{
#age = 0; // private field property with a default value
constructor(name, species) {
this.name = name;
this.species = species;
}
}
let cow = new Animal('bruce', 'cow');
cow.age = 20; // not allowed
Again, this is not a fully supported feature yet and should not be used on any production level code. The benefit of private properties is that you can still have data fields that are required for your code to function without exposing them to any client code.
And there you have it folks. A more formal way to implement classes and object principles in JavaScript. I hope you found this post helpful and if so, leave me a comment, send me a message and hit the like button down below to get more related topics in a future post.