Creating private members in JavaScript

The private keyword in an object-oriented programming language is an access modifier that can be used to make properties and methods accessible only in declared classes. 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 similar functions in JavaScript? The keyword private is not reserved, but in the new standard, JavaScript has its own way to Create private member of class However, it is still in the trial draft of ES2020, and the syntax is strange, with # as the prefix. Here are several ways to implement private properties and methods in JavaScript code.

Use closures

Using closures, you can use the encapsulation of private properties or methods. Using closures, you can access the 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()); // DevPoint

This can be translated into assigning the top-level self calling function call to a variable and exposing some of its internal functions only by 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()); // 360

Use ES6 class

To make the 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 the class. Use class to refactor the ButtonCreator example above:

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()); // 360

Now suppose that the properties are public, but you want to use them in private methods, where the context points to ButtonCreator. You can implement it in the following ways:

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()); // 360

The above code uses Function.prototype.call , which is used to call a function with a given context. In the example, the context of the ButtonCreator class is used. If private functions also require parameters, they can be passed as additional parameters to call:

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()); // 60

Use ES2020 proposal

It is also in the ES2020 pilot draft, which introduces the definition of private methods or attributes. The syntax is strange, with # as the prefix.

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()); // 600

Using WeakMap

This method is based on the closure method, uses the scope variable method to create a private WeakMap, and then uses the WeakMap to retrieve the private data related to it. This is faster than the scope 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 attribute
            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()); // 600

This approach is a bit awkward for the use of private methods.

Using TypeScript

TypeScript can be used as a style of JavaScript, 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'.

summary

This article summarizes several methods of re creating private attributes in JavaScript. See what you like.

Keywords: Javascript TypeScript prototype

Added by chuspy on Wed, 08 Dec 2021 02:18:35 +0200