Understanding Private Properties in JavaScript

In the topic of object-oriented programming, the properties and methods can be divided into two categories, private and public.

The public properties are those that can be accessed from outside of the object. All of the examples we've seen so far are public properties.

Private properties cannot be accessed from the outside and can only be used by methods in the same class.

Most other programming languages offer a language-level way to define private properties, usually with the keyword private. But this feature has been missing in JavaScript for a long time.

Private properties

So JavaScript developers decided on a convention, that is, if a property name starts with an underscore (_), that property is considered private and should not be accessed from the outside.

javascript
1class User {
2  constructor(name) {
3    this._name = name; // This property is private
4    this.status = "Admin"; // This property is public
5  }
6}

Technically, this _name property can still be accessed.

javascript
1let user = new User("John Doe");
2
3console.log(user._name);
text
1John Doe

But you shouldn't do that in your codebase to avoid security risks. Instead, create a name setter to manipulate the _name property.

javascript
1class User {
2  constructor() {
3    this._name;
4  }
5
6  set name(name) {
7    this._name = name;
8  }
9}
10
11let user = new User();
12
13user.name = "John Doe";
14
15console.log(user);
text
1User { _name: 'John Doe' }

The reason that this is safer is that you can add a verification mechanism in the setter.

javascript
1class User {
2  constructor() {
3    this._name;
4  }
5
6  set name(name) {
7    if (name.length > 5 && name.length < 15) {
8      this._name = name;
9    } else {
10      this._name = "John Doe";
11    }
12  }
13}
14
15let user = new User();
16
17user.name = "Alice Cooper";
18console.log(user);
19
20user.name = "qwertyuiopasdfghjklzxcvbnm";
21console.log(user);
text
1User { _name: 'Alice Cooper' }
2User { _name: 'John Doe' }

This makes sure that the data is verified before the actual _name property is altered. In this case, only strings longer than five characters and shorter than 15 characters will be accepted. If the input is invalid, "John Doe" will be set as the _name.

If you want to retrieve the _name property, use a name getter instead of using user._name.

javascript
1class User {
2  constructor() {
3    this._name;
4  }
5
6  get name() {
7    return this._name;
8  }
9
10  set name(name) {
11    if (name.length > 5 && name.length < 15) {
12      this._name = name;
13    } else {
14      this._name = "John Doe";
15    }
16  }
17}
18
19let user = new User();
20
21user.name = "Alice Cooper";
22
23console.log(user.name);
text
1Alice Cooper

Private methods

Methods can also be private, which are often helper functions for other methods. They are defined in a similar way.

javascript
1class User {
2  constructor() {
3    this._name;
4  }
5
6  _private() {
7    console.log("This method shouldn't be accessed from the outside.");
8  }
9}

Again, the underscore syntax is only a convention that JavaScript developers have agreed upon. It is not supported on a language level. They can still be accessed from the outside, but you shouldn't.

Language level support is coming

The language-level support for private properties and methods might be coming to JavaScript very soon. It is specified in a proposal, but not yet included in the official standard.

Instead of an underscore, this syntax uses a #. And instead of defining properties in the constructor(), private properties are defined at the same level as the methods.

javascript
1class User {
2  constructor() {
3    this.status = "Admin";
4  }
5
6  #name; // This is a private property
7
8  get name() {
9    return this.#name; // The private property can only be accessed from the inside with "this".
10  }
11
12  set name(name) {
13    if (name.length > 5 && name.length < 15) {
14      this.#name = name;
15    } else {
16      this.#name = "John Doe";
17    }
18  }
19
20  #private() {
21    console.log("This method couldn't be accessed from the outside.");
22  }
23
24  public() {
25    this.#private();
26    console.log("But can be accessed by another method in the same class.");
27  }
28}
29
30let user = new User();
31
32user.name = "Alice Cooper";
33
34console.log(user.name); // -> Alice Cooper
35user.public();
text
1Alice Cooper
2This method couldn't be accessed from the outside.
3But can be accessed by another method in the same class.