The private keyword in object-oriented programming languages is an access modifier that can be used to make properties and methods accessible only in the declared class. This makes it easy to hide the underlying logic, which should be hidden and should not interact with the outside of the class.

But how do you implement something similar in JavaScript? The keyword private is not retained, but JavaScript has its own way to create class private members in the new standard, which is currently in the ES2020 pilot draft and has a strange syntax prefixed with #. Here are a few ways to implement private properties and methods in JavaScript code.

Using closures

Closures can use encapsulation of private properties or methods. Closures are used to access variable characteristics of external functions. The following code snippet:

function MyProfile() { const myTitle = "DevPoint"; return { getTitle: function () { return myTitle; }}; } const myProfile = MyProfile(); console.log(myProfile.getTitle()); // DevPointCopy the code

This translates to assigning the top-level self-calling function call to a variable and exposing some of its internal functions only with the function return:

const ButtonCreator = (function () { const properties = { width: 100, height: 50, }; const getWidth = () => properties.width; const getHeight = () => properties.height; const setWidth = (width) => (properties.width = width); const setHeight = (height) => (properties.height = height); return function (width, height) { properties.width = width; properties.height = height; return { getWidth, getHeight, setWidth, setHeight, }; }; }) (); const button = new ButtonCreator(600, 360); console.log(button.getHeight()); / / 360Copy the code

Use the ES6 class

To make your code more similar to OOP methods, you can use the class keyword introduced in ES6. To make properties and methods private, you can define them outside of the class. For the ButtonCreator example above, use class to refactor:

const properties = { width: 100, height: 50, }; class ButtonCreator { constructor(width, height) { properties.width = width; properties.height = height; } getWidth = () => properties.width; getHeight = () => properties.height; setWidth = (width) => (properties.width = width); setHeight = (height) => (properties.height = height); } const button = new ButtonCreator(600, 360); console.log(button.getHeight()); / / 360Copy the code

Now assume that the properties are public, but want to use them in a private method where the context points to ButtonCreator, you can implement it as follows:

const privates = { calculateWidth() { return this.width; }}; class ButtonCreator { constructor(width, height) { this.width = width; this.height = height; } getWidth = () => privates.calculateWidth.call(this); getHeight = () => this.height; setWidth = (width) => (this.width = width); setHeight = (height) => (this.height = height); } const button = new ButtonCreator(600, 360); console.log(button.getHeight()); / / 360Copy the code

The above code uses function.prototype. call, which is used to call a Function with a given context. In this example, use the context of the ButtonCreator class. If a private function also needs arguments, it can be called by passing them as additional arguments:

const privates = { calculateWidth(percent) { return this.width * percent; }}; class ButtonCreator { constructor(width, height) { this.width = width; this.height = height; } getWidth () = = > privates. CalculateWidth. Call (this, 0.1); getHeight = () => this.height; setWidth = (width) => (this.width = width); setHeight = (height) => (this.height = height); } const button = new ButtonCreator(600, 360); console.log(button.getWidth()); / / 60Copy the code

Use the ES2020 proposal

Also in the ES2020 pilot draft, introduced the definition of a private method or property with a weird syntax prefixed with #.

class ButtonCreator { #width; #height; constructor(width, height) { this.#width = width; this.#height = height; } // Private method #calculateWidth() {return this.#width; } getWidth = () => this.#calculateWidth(); getHeight = () => this.#height; setWidth = (width) => (this.#width = width); setHeight = (height) => (this.#height = height); } const button = new ButtonCreator(600, 360); console.log(button.width); // undefined console.log(button.getWidth()); / / 600Copy the code

Using WeakMap

This approach builds on the closure method, using the scoped variable method to create a private WeakMap, which is then used to retrieve the private data associated with it. This is faster than the scoped variable method because all instances can share a WeakMap, so you don’t need to recreate the method every time you create an instance.

const ButtonCreator = (function () { const privateProps = new WeakMap(); class ButtonCreator { constructor(width, height, name) { this.name = name; // Public property privateProps. Set (this, {width, // private property height, // private property calculateWidth: () => privateProps. Get (this).width, // private method}); } getWidth = () => privateProps.get(this).calculateWidth(); getHeight = () => privateProps.get(this).height; } return ButtonCreator; }) (); const button = new ButtonCreator(600, 360); console.log(button.width); // undefined console.log(button.getWidth()); / / 600Copy the code

This approach is somewhat awkward for private methods.

Use the TypeScript

You can use TypeScript as a JavaScript style, and you can really recreate functionality from an object-oriented language using the private keyword.

class ButtonCreator {
    private width: number;
    private height: number;
    constructor(width: number, height: number) {
        this.width = width;
        this.height = height;
    }
    private calculateWidth() {
        return this.width;
    }
    public getWidth() {
        return this.calculateWidth();
    }
    public getHeight() {
        return this.height;
    }
}
const button = new ButtonCreator(600, 360);

console.log(button.getWidth()); // 600
console.log(button.width); // error TS2341: Property 'width' is private and only accessible within class 'ButtonCreator'.
Copy the code

conclusion

This article summarizes several ways to create private attributes in JavaScript, depending on your personal preference.