Even though ES6 introduced the class
keyword that fairly well mimics classes and allows us to jump into object-oriented programming, JavaScript is missing the ability to create public, private, and protected members in a class.
If you have worked with any object-oriented language, you must know the importance of internal vs external interface. Internal interface refers to the methods and properties of a class that is only accessible by the class itself and not from outside. In contrast, the external interface has methods and properties that are also accessible from outside the class.
The three major keywords at play are public, protected, and private.
- Public: These members of the class and available to everyone that can access the (owner) class instance.
- Private: These members are only accessible within the class that instantiated the object.
- Protected: This keyword allows a little more access than private members but a lot less than the public. A protected member is accessible within the class (similar to private) and any object that inherits from it. A protected value is shared across all layers of the prototype chain. It is not accessible by anybody else.
The protected keyword is the hardest keyword of the three to imitate in JavaScript.
Public
This is the default nature of JavaScript. If something has access to an object, it does have access to its members. Example:
const myObject = {
name: "Parwinder",
sayMyName: function () {
return this.name;
}
}
console.log(myObject.name); // Parwinder
console.log(myObject.sayMyName()); // Parwinder
In the above example, I can access the property and method without any issue. If you would rather prefer it in a class syntax:
class ObjectCreator {
name;
constructor(name) {
this.name = name;
}
sayMyName() {
return this.name;
}
}
const myObject = new ObjectCreator("Parwinder");
console.log(myObject.name); // Parwinder
console.log(myObject.sayMyName()); // Parwinder
Private
There are multiple ways of creating private variables in JavaScript. First is closures.
function carMonitor() {
var speed = 0;
return {
accelerate: function () {
return speed++;
}
}
}
var car = new carMonitor();
var redCar = new carMonitor()
console.log(car.accelerate()); // 0
console.log(car.accelerate()); // 1
console.log(redCar.accelerate()); // 0
console.log(redCar.accelerate()); // 1
console.log(car.accelerate()); // 2
console.log(redCar.accelerate()); // 2
console.log(speed); // speed is not defined
car
and redCar
maintain their own private speed
variables, and speed
is not accessible outside. We are enforcing the consumer to use the methods defined on the function or class rather than accessing the properties directly (which they should not). This is how you would encapsulate your code.
The second way is by using the #
notation.
class ObjectCreator {
#meaningOfLife;
constructor(name) {
this.#meaningOfLife = 42;
}
returnMeaningOfLife() {
return this.#meaningOfLife;
}
#returnAMessage() {
return "You will do great things in life";
}
}
const myObject = new ObjectCreator("Parwinder");
console.log(myObject.returnMeaningOfLife()); // 42
console.log(myObject["#meaningOfLife"]); // undefined
console.log(myObject.#meaningOfLife); // SyntaxError
console.log(myObject.#returnAMessage); // SyntaxError
The language enforces encapsulation. It is a syntax error to refer to #
names from out of scope. Public and private fields do not conflict. We can have both private #meaningOfLife and public meaningOfLife fields in the same class.
🚨 The #
method for declaring private members of a class is in part of ES2019/ES10.
Protected
Like I said at the beginning of this post, protected is the hardest of all 3 to implement in JavaScript. The only way that I can think of doing this is by using a class that has a getter for a property without a setter. The property will be read-only, and any object will inherit it from the class, but it will only be change-able from within the class itself.
🙏 If you have an example of creating protected members of the class (or as close to protected as we can get), please do share!
class NameGenerator {
_name;
constructor(name) {
this._name = name;
}
get name() {
return this._name;
}
}
let nameGenerator = new NameGenerator("John");
console.log(`My name is ${nameGenerator.name}`); // My name is John
nameGenerator.name = "Jane"; // Cannot assign to 'name' because it is a read-only property.
Top comments (7)
This is my crack at the problem. I think this works back to es3 but I'm not sure?
Seems very promising. Now examining the code! :O
Have fun! I'd like to add that in practice I'd probably not go to these length to protect the variables unless it was absolutely necessary. I'd probably do something more like:
Yo use it the same, you just have to trust people not to pass in a
_
argument on construction to snatch the shared variables. I think it's a step up from defining public variables likethis._myProtectedVar
, but it's not technically protected (which is why I call the variableshared
inside the function).I have managed to emulate an extended protected class (somewhat) using a function that contains ES6's magic to destructure an Object from another function and an Object.assign to extend the Object on return.
The code in question:
A bit late, but check out my package
lowclass
, and the unit tests that depict the possible ways to use it:github.com/trusktr/lowclass
It has, among other things, a class implementation that creates a runtime abstraction of encapsulation, included protected and private.
This example shows protected works:
Private
works much the same way, but as you guessed it, usable only in the class where it is defined.We have the new
#private
syntax in JavaScript classes now, but it has a fairly big downside in that it will inadvertently break code when an instance with private fields is passed to other code that wraps the object withProxy
(many frameworks today useProxy
):lea.verou.me/2023/04/private-field...
Built-in
protected
is nowhere to be seen. The TC39 people working onclass
syntax don't like the concept of "protected" because "if you can extend from a class and access a protected member, then it is public, so we don't need protected".Hello,
not sure how much my code is correct in terms of using private properties in the super class simulating the "protected behavior", but considering that protected properties are not natively supported, this is the trick I found and which works for my little project. Hope this can help.
in short, I declare the "protected" properties as private in the super class and in the derivated classes I can get/set the values using getter and setter from the super class.
Somehow, using these methods it works and each derivated class has its own values (which I can never access directly with "this" but I have always to go through the getters and setters).
Below the example code.
I'd like to have your opinion on that.
thanks guys and happy coding.
For protected variables you could also kind of use what was used for private variables with a function.
Though I don't have any good idea on how to actually change the values instead of creating a new object;