Public, private and protected class visibility modes in javascript
Public Class
As part of ES2015
, it supports public fields (no field syntax), public getters
/ setters
and public methods, as well as public static getters
/ setters
and public static methods.
Here is an example that includes all the methods mentioned above:
class Person {<!-- --> constructor(firstName, lastName) {<!-- --> this.firstName = firstName; // Public field this.lastName = lastName; // Public field } // public getter get fullName() {<!-- --> return `${<!-- -->this.firstName} ${<!-- -->this.lastName}`; } // public setter set fullName(value) {<!-- --> const parts = value.split(" "); this.firstName = parts[0]; this.lastName = parts[1]; } // public method introduceYourselfTo(other) {<!-- --> const name = other.firstName other; console.log(`Hello ${<!-- -->name}! My name is ${<!-- -->this.fullName}.`); } // public static getter static get typeName() {<!-- --> return "Person"; } // public static method static fromJSON(json) {<!-- --> return new Person(json.firstName, json.lastName); } } const john = new Person("leo", "lau"); const jane = Person.fromJSON({<!-- --> firstName: "leo", lastName: "lau" }); john.introduceYourselfTo('jack'); </code><img class="look-more-preCode contentImg-no-view" src="//i2.wp.com/csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreBlack. png" alt="" title="">
This version of class
is familiar to most js
developers today. It was the first solid set of class features of the language, but several things were missing. One of the missing features is the syntax of the fields. In subsequent versions of the language, this problem was solved by enabling public fields and public static field syntax. Here’s an example:
class Person {<!-- --> age = 0; //Field syntax static typeName = "Person"; // Syntax of public fields shareYourAge() {<!-- --> console.log(`I am ${<!-- -->this.age} years old.`); } } const john = new Person("leo", "lau"); leo.age = 25; leo.shareYourAge();
While designing the decorator, a supporting function for the automatic accessor was also developed. Just use the new accessor
keyword:
class Person {<!-- --> accessor age = 0; } const john = new Person("leo", "lau"); leo.age = 25; leo.shareYourAge();
Private Class
In July 2021, major browsers will support private class
, bringing protected private functions into the language. Private members all start with the #
character and can only be called statically. Here is an example:
class Person {<!-- --> #firstName; // Private property #lastName; // Private property constructor(firstName, lastName) {<!-- --> this.#firstName = firstName; this.#lastName = lastName; } get firstName() {<!-- --> return this.#firstName; } get lastName() {<!-- --> return this.#lastName; } get fullName() {<!-- --> return `${<!-- -->this.firstName} ${<!-- -->this.lastName}`; } introduceYourselfTo(other) {<!-- --> const name = other.firstName other; console.log(`Hello ${<!-- -->name}! My name is ${<!-- -->this.fullName}.`); } static fromJSON(json) {<!-- --> return new Person(json.firstName, json.lastName); } } </code><img class="look-more-preCode contentImg-no-view" src="//i2.wp.com/csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreBlack. png" alt="" title="">
By combining private fields with public getters
, js
now also supports platform-protected read-only properties shown above.
Private Constructors
Fields, getters
, setters
and methods, instances and statics can be made private, but constructors cannot. But a pattern can be used to prevent unauthorized calls to the constructor.
class SomeSingletonService {<!-- --> static #key = {<!-- -->}; static instance = new SomeSingletonService(this.#key); constructor(key) {<!-- --> if (key !== SomeSingletonService.#key) {<!-- --> throw new TypeError("SomeSingletonService is not constructable."); } } }
The important detail in this pattern is that we hold a private static key as the first parameter of the constructor. If the key is not provided to unlock the constructor, an exception is thrown. Now our class can create instances at will using its private key, but any other party trying to instantiate via the constructor is blocked.
Protected Class
Although JavaScript
does not provide this feature, we can use some methods to handle private constructor methods. Here’s how it works:
// Parent class: function Question(key) {<!-- --> return class {<!-- --> #answer = 42; answer(shareKey) {<!-- --> if (shareKey === key) {<!-- --> return this.#answer; } throw new TypeError("Access Denied"); } } } const key = {<!-- -->}; // Subclass class DeepThought extends Question(key) {<!-- --> get #answer() {<!-- --> return this.answer(key); } tellMeTheAnswer() {<!-- --> console.log(this.#answer); } } const dm = new DeepThought(); dm.tellMeTheAnswer(); </code><img class="look-more-preCode contentImg-no-view" src="//i2.wp.com/csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreBlack. png" alt="" title="">
We first create a factory function Question
that takes a key as input and will use that key to check access to any shared value it provides. We then create a unique key and pass it to the superclass factory as part of the subclass definition. In the subclass, we create a private property for accessing the shared value, which internally calls the superclass shared method using a predefined key. To avoid making protected members public, we also make our subclasses private.
Use decorators
The above code is for protected scenarios as well as arbitrary sharing, just by sharing a secret. However, some template code is not particularly expressive and can even be a bit confusing. We can do this by using the decorator:
function Question(share) {<!-- --> return class {<!-- --> @share accessor #answer = 42; } } class DeepThought extends Question(share) {<!-- --> @access accessor #answer; tellMeTheAnswer() {<!-- --> console.log(this.#answer); } } const dm = new DeepThought(); dm.tellMeTheAnswer(); </code><img class="look-more-preCode contentImg-no-view" src="//i2.wp.com/csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreBlack. png" alt="" title="">
Using decorators makes it clearer what values are shared by superclasses and what values are accessible by subclasses. It also eliminates some of the confusion caused by similar names in the previous version and is more suitable for multiple members.
The technique to achieve this is to use a factory function for two decorators that share a common private accessor store.