

Doing it Right: Private Class Properties in JavaScript
source link: https://www.tuicool.com/articles/NRB3Mbv
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

A look at the class fields proposals and the existing workarounds
Oct 22 ·7min read
The class syntax has been one of the most popular features introduced in ES2015. However, the lack of native support for private properties has hindered it from reaching its full potential. Although the JavaScript community has come up with various patterns and workarounds to implement similar functionality, they’re still not as easy-to-use as a native implementation.
Fortunately, there’s a proposal that would simplify the process of creating private properties. The proposal, called class fields, is currently at stage 3, which means its syntax, semantics, and API are completed. Chrome 74+ is the only browser that fully supports class fields at the moment. But you can use a transpiler like Babel to implement them on older platforms.
Compared to existing workarounds, class fields are much easier to use. However, it’s still important to be aware of how other patterns work. When working on a project, you may have to work on another programmer’s code that’s not up-to-date. In such a situation, you need to know how the code works and how to upgrade it.
Therefore, before delving into the class fields, we take a look at existing workarounds and their shortcomings.
Tip: Share and reuse utility and UI components
Use Bit to easily publish, install and update small individual modules across different JavaScript projects. Don’t install entire libraries and don’t copy-paste code, when you can build faster with small reusable modules. Take a look .

PrivatePropertiesin ES2015
There are three popular ways of creating private class properties in ES2015.
A Leading Underscore
Using a leading underscore (_) is a common convention to show that a property is private. Although such a property isn’t really private, it’s generally adequate to signal to a programmer that the property shouldn’t be modified. Let’s look at an example:
class SmallRectangle { constructor() { this._width = 20; this._height = 10; } get dimension() { return { width: this._width, height: this._height }; } increaseSize() { this._width++; this._height++; } }
This class has a constructor that creates two instance properties: _width
and _height
, both of which are considered private. But because these properties can be overwritten accidentally, this approach is not reliable for creating private class properties:
const rectangle = new SmallRectangle();console.log(rectangle.dimension); // => {width: 20, height: 10}rectangle._width = 0; rectangle._height = 50;console.log(rectangle.dimension); // => {width: 0, height: 50}
Scoped Variables
A safer way to make private members is to declare variables inside the class constructor instead of attaching them to the new instance being created:
class SmallRectangle { constructor() { let width = 20; let height = 10; this.getDimension = () => { return {width: width, height: height}; }; this.increaseSize = () => { width++; height++; }; } }const rectangle = new SmallRectangle();console.log(rectangle.getDimension()); // => {width: 20, height: 10}// here we cannot access height and width console.log(rectangle.height); // => undefined console.log(rectangle.width); // => undefined
In this code, the scope of the constructor function is used to store private variables. Since the scope is private, you can truly hide variables that you don’t want to be publicly accessible. But it’s still not an ideal solution: getDimension()
and increaseSize()
are now created on every instance of SmallRectangle
.
Unlike methods defined on the prototype, which are shared, methods defined on instances take up separate space in the environment’s memory. This won’t be a problem if your program creates a small number of instances, but complex programs that require hundreds of instances will be slowed down.
ScopedWeakMaps
Compared to a Map
, a WeakMap
has a more limited feature set. Because it doesn’t provide any method to iterate over the collection, the only way to access a value is to use the reference that points to the value’s key.
Additionally, only objects can be used as keys. But in exchange for these limitations, the keys in a WeakMap
are weakly referenced, meaning that the WeakMap
automatically removes the values when the object keys are garbage collected. This makes WeakMaps
very useful for creating private variables. Here’s an example:
const SmallRectangle = (() => { const pvtWidth = new WeakMap(); const pvtHeight = new WeakMap(); class SmallRectangle { constructor(name) { pvtWidth.set(this, 20); // private pvtHeight.set(this, 10); // private } get dimension() { return { width: pvtWidth.get(this), height: pvtHeight.get(this) }; } increaseSize() { pvtWidth.set(this, pvtWidth.get(this) + 1); pvtHeight.set(this, pvtHeight.get(this) + 1); } } return SmallRectangle; })();const rectangle = new SmallRectangle();// here we can access public properties but not private ones console.log(rectangle.width); // => undefined console.log(rectangle.height); // => undefined console.log(rectangle.dimension); // => {width: 20, height: 10}
This technique has all the benefits of the previous approach but doesn’t incur a performance penalty. That being said, the process is unnecessarily complicated, given many other languages provide a native API that’s very simple to use.
Class Fields Proposal
Class fields are designed to simplify the process of creating private class properties. The syntax is very simple: add a # before the name of a property, and it will become private.
Using private fields, we can rewrite the previous example like this:
class SmallRectangle { #width = 20; #height = 10; get dimension() { return {width: this.#width, height: this.#height}; } increaseSize() { this.#width++; this.#height++; } }const rectangle = new SmallRectangle();console.log(rectangle.dimension); // => {width: 20, height: 10}rectangle.#width = 0; // => SyntaxError rrectangle.#height = 50; // => SyntaxError
Note that you also have to use #
when you want to access a private field. As of this writing, it’s not possible to use this syntax to define private methods and accessors. There’s another stage 3 proposal , however, that aims to fix that. Here’s how it would work:
class SmallRectangle { #width = 20; #height = 10; // a private getter get #dimension() { return {width: this.#width, height: this.#height}; } // a private method #increaseSize() { this.#width++; this.#height++; } }
It’s important to keep in mind that the name of private fields cannot be computed. Attempting to do so throws a SyntaxError
:
const props = ['width', 'height'];class SmallRectangle { #[props[1]] = 20; // => SyntaxError #[props[0]] = 10; // => SyntaxError }
To simplify the class definition, the proposal also provides a new way to create public properties. Now you can declare public properties directly in the class body. So, a constructor function isn’t required anymore. For example:
class SmallRectangle { width = 20; // a public field height = 10; // another public field get dimension() { return {width: this.width, height: this.height}; } }const rectangle = new SmallRectangle();rectangle.width = 100; rectangle.height = 50;console.log(rectangle.dimension); // => {width: 100, height: 50}
As you can see, public fields are created in a similar way to private fields except that they don’t have a hashtag.
Inheritance
Class fields also provide a simpler subclassing. Consider the following example:
class Person { constructor(param1, param2) { this.firstName = param1; this.lastName = param2; } }class Student extends Person { constructor(param1, param2) { super(param1, param2); this.schoolName = 'Princeton'; } }
This code is very straightforward. The Student
class inherits from Person
and adds an additional instance property. With public fields, creating a subclass can be simplified like this:
class Person { constructor(param1, param2) { this.firstName = param1; this.lastName = param2; } }class Student extends Person { schoolName = 'Princeton'; // a public class field }const student = new Student('John', 'Smith');console.log(student.firstName); // => John console.log(student.lastName); // => Smith console.log(student.schoolName); // => Princeton
Therefore, it’s no longer necessary to call super()
to execute the constructor of the base class or put the code in the constructor function.
Static Class Fields
You can make a class field static by preceding it with the static
keyword. A static class field is called on the class itself, as shown in this example:
class Car { static #topSpeed = 200; // convert mile to kilometer convertMiToKm(mile) { const km = mile * 1.609344; return km; } getTopSpeedInKm() { return this.convertMiToKm(Car.#topSpeed); } }const myCar = new Car; myCar.getTopSpeedInKm(); // => 321.8688
Keep in mind that static class fields are linked to the class itself, not instances. To access a static field, you must use the name of the class (instead of this
).
Conclusion
In this post, we’ve taken a good look at the new and existing ways of creating private class properties in JavaScript. We learned about the shortcomings of existing methods and saw how the class fields proposal tries to fix and simplify the process.
As of this writing, you can use class fields in Chrome 74+ and Node.js 12 without having to use flags or transpilers. Firefox 69 supports public class fields but not private once. Wider browser support is expected soon. Until then, you can use babel to implement the feature on web browsers that do not support it yet.
Recommend
-
143
Learning Java, am In doing this right? Add to FavoritesShareLear...
-
142
Explore Posts
-
17
One of the best aspects of ZFS is its reliability. This can be accomplished using a few features like copy-on-write approach and checksumming. Today we will look at how ZFS does checksumming and why it does it the proper...
-
7
-
11
Doing the right thing when someone else's code breaks Here's a scenario which is bound to arise in any large organization where software is a nontrivial part of the business. You will be the user of tools which are created by ano...
-
7
JS prototype class, not all properties are accessible advertisements I have defined a prototype class with few properties and methods and for...
-
5
Javascript & ldquo; Private & rdquo; Vs properties of the instance advertisements I'm doing some Javascript R&D and,...
-
11
Lean Admin is a Laravel package for building custom admin panels. It's based on the TALLstack (Livewire & Alpine) and its main focus is customizability. Start by defining re...
-
3
June 20, 2023 /
-
5
What is Proxy? The JS
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK