JavaScript classes, which were added in ECMAScript 2015 (ES6), are a way of creating objects with a common structure and behavior. They can have different kinds of members, such as properties (fields) and methods (functions).
By default, class members are public, which means they can be accessed from anywhere. However, you can also create private class members by using a hash # prefix. Private members are only accessible within the class itself and cannot be inherited by subclasses.
JavaScript doesn’t have native support for protected members, which are accessible within the class and its subclasses but not outside. However, you can emulate this behavior by using symbols or weak maps.
Now, it’s time to see some code.
Public Properties and Methods
Public properties and methods of a class are declared without any prefixes. They can be accessed by any code that has a reference to the class instance.
Example:
class Website {
// public field
name;
// public method
constructor(name) {
this.name = name;
}
// public method
greet() {
console.log(`Hello, welcome to ${this.name}.`);
}
}
let obj = new Website('slingacademy.com');
obj.greet()
Output:
Hello, welcome to slingacademy.com.
Private Members
Private members are declared with a hash # prefix. They can only be accessed within the class body. They are not inherited by subclasses (don’t forget that or you will be very likely to run into a great deal of trouble in the future).
Example:
class Counter {
// this is a private property
#count = 0;
increment() {
// only methods of this class can access this member
this.#count++;
}
// we need a public method to get the value of the private member
getCount() {
return this.#count;
}
}
let counter = new Counter();
counter.increment();
counter.increment();
console.log(counter.getCount());
// output: 2
If you try to access the private field #count outside the class like this:
console.log(counter.#count);
You’ll receive this error:
Uncaught SyntaxError: Private field '#count' must be declared in an enclosing class
Protected Members
As mentioned above, protected members are not supported by JavaScript natively.
However, many JavaScript developers use the underscore character _ for protected members as a convention or a naming pattern. It is not enforced by the language itself. The underscore prefix indicates that the member is intended to be used only by the class itself or its subclasses, but not by other code. It is a way of signaling to other developers that they should respect the encapsulation principle and not access or modify those members directly.
It is important to note that the convention does not prevent anyone from accessing or modifying those members if they really want to. It is just a matter of good practice and code readability.
Example:
class Animal {
constructor(name) {
// this is a protected member, indicated by the _ prefix
this._name = name;
}
getName() {
// we can access the protected member from within the class or subclasses
return this._name;
}
}
class Dog extends Animal {
constructor(name, breed) {
// we call the parent constructor with the name argument
super(name);
// this is another protected member
this._breed = breed;
}
getBreed() {
// we can access the protected member from within the class or subclasses
return this._breed;
}
introduce() {
// we can access both protected members from within the subclass
console.log(`I am ${this.getName()}, a ${this.getBreed()} dog.`);
}
}
let dog = new Dog('Spot', 'Labrador');
dog.introduce();
console.log(dog.getName());
// this works, but this is not recommended as it violates encapsulation principles
console.log(dog._name);
// this works, but again this is not recommended for the same reason
console.log(dog._breed);
Output:
I am Spot, a Labrador dog.
script.js:36 Spot
script.js:39 Spot
script.js:42 Labrador
Conclusion
In general, you will work with public properties and methods the most, then less often than with private members. Cases involving protected members are quite rare and should be used with caution with unambiguous comments.