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.