Implementing Private Variables In JavaScript
JavaScript, the programming language that empowers the World Wide Web, has become a widely used and versatile technology since its creation by Brendan Eich in May 1995. Despite its success, it has also received considerable criticism, especially some features. For example, an object is cast to a string when used as an index, 1 == "1" returns true, or the infamous confusing this
keyword. However, one particularly interesting feature is the existence of various techniques that implement variable privacy.
Currently, there is no way to directly create private variables in JavaScript. In other languages, you can use private
keywords or double underscores and everything works fine, but in JavaScript, variable privateness has features that make it more similar to the emergent features of the language than the expected functionality. Let's first introduce the background of our question.
var
keyword
Until 2015, there was basically only one way to create variables, and that was the var
keyword. var
is function scoped, which means that variables instantiated with this keyword can only be accessed by code within the function. In the case where the function is external or essentially "global", the variable will be accessible by anything executed after the variable is defined. If you try to access the variable in the same scope before its definition, you will get undefined
instead of an error. This is due to the "upgrade" way of the var
keyword.
// Define "a" in global scope var a = 123; // Define "b" in function scope (function() { console.log(b); //=> Due to promotion, "undefined" is returned instead of an error. var b = 456; })(); console.log(a); // => 123 console.log(b); // Throws a "ReferenceError" exception because "b" cannot be accessed from outside the function scope.
The birth of ES6 variables
In 2015, ES6/ES2015 was officially released, and followed by two new variable keywords: let
and const
. Both are block-scoped, meaning that variables created with these keywords can be accessed by anything within the same pair of brackets. Same as var
, but let
and const
variables cannot be accessed outside block scopes such as loops, functions, if statements, brackets, etc.
const a = 123; // Block Scope Example #1 if (true) { const b = 345; } // Block Scope Example #2 { const c = 678; } console.log(a); // 123 console.log(b); // Throws "ReferenceError" because "b" cannot be accessed from outside the block scope. console.log(c); // Throws "ReferenceError" because "b" cannot be accessed from outside the block scope.
Since code outside the scope cannot access variables, we obtain the emergence of privateness. We will introduce some techniques to implement it in different ways.
Using functions
Since functions in JavaScript are also blocks, all variable keywords work with them. Furthermore, we can implement a very useful design pattern called "module".
Module design mode
Google relies on the Oxford Dictionary to define "modules":
The program may construct from it or may analyze any of a number of different but interrelated units of complex activities.
— “Module” definition 1.2
The module design pattern is very useful in JavaScript because it combines public and private components and allows us to break down programs into smaller components, exposing only what another program part should be able to access through a process called "encapsulation". In this way, we only expose what we need to use and hide what we don’t need to see. We can do this by leveraging the function scope.
const CarModule = () => { let milesDriven = 0; let speed = 0; const accelerate = (amount) => { speed = amount; milesDriven = speed; } const getMilesDriven = () => milesDriven; // Using the "return" keyword, you can control what content is exposed and what content is hidden. In this case, we expose only the accelerate() and getMilesDriven() functions. return { accelerate, getMilesDriven } }; const testCarModule = CarModule(); testCarModule.accelerate(5); testCarModule.accelerate(4); console.log(testCarModule.getMilesDriven());
This way we can get mileage and acceleration, but since the user does not need access speed in this case, we can hide it by exposing only accelerate()
and getMilesDriven()
methods. Essentially, speed
is a private variable because it can only be accessed by code within the same scope. The benefits of private variables start to become clear in this case. When you delete the ability to access variables, functions, or any other internal components, you reduce the surface area that is wrongly used by others by mistake by others that should not have been used.
Another way
In this second example, you will notice the addition of this
keyword. There is a difference between the ES6 arrow function (=>) and the traditional function (){}. With function
keyword you can use this
, which will bind to the function itself, and the arrow function does not allow the use of any type of this
keyword. Both are equally effective ways to create modules. The core idea is to disclose the parts that should be accessed and retain other parts that should not be interacted with, so there is both public and private data.
function CarModule() { let milesDriven = 0; let speed = 0; // In this case, we use the "this" keyword instead, // It refers to CarModule this.accelerate = (amount) => { speed = amount; milesDriven = speed; } this.getMilesDriven = () => milesDriven; } const testCarModule = new CarModule(); testCarModule.accelerate(5); testCarModule.accelerate(4); console.log(testCarModule.getMilesDriven());
ES6 Class
Classes are another new feature in ES6. Classes are essentially syntactic sugar—in other words, still a function, but may "beautify" it into a more easily expressed form. For classes, variable privateness (as of now) is nearly impossible unless some major changes are made to the code.
Let's look at an example of a class.
class CarModule { /* milesDriven = 0; speed = 0; */ constructor() { this.milesDriven = 0; this.speed = 0; } accelerate(amount) { this.speed = amount; this.milesDriven = this.speed; } getMilesDriven() { return this.milesDriven; } } const testCarModule = new CarModule(); testCarModule.accelerate(5); testCarModule.accelerate(4); console.log(testCarModule.getMilesDriven());
The first thing to note is that milesDriven
and speed
variables are located in constructor()
function. Note that you can also define variables outside of the constructor (as shown in the code comments), but they are functionally the same anyway. The problem is that these variables will be public and can be accessed by elements outside the class.
Let's take a look at some solutions to this problem.
Use underline
In situations where privateness is to prevent collaborators from making some catastrophic mistakes, using an underscore (_) as a variable prefix, although it is still "visible" to the outside, is enough to signal the developer, "Don't touch this variable". So, for example, we now have the following:
// This is the new constructor of the class. Note that it can also be represented as the following content outside the constructor(). /* _milesDriven = 0; _speed = 0; */ constructor() { this._milesDriven = 0; this._speed = 0; }
While this works for its specific use cases, it is still safe to say that it is not ideal in many ways. You can still access the variables, but you also have to modify the variable name.
Put everything in the constructor
Technically, there is indeed a way to use private variables in a class, which is to put all variables and methods in constructor()
function. Let's take a look.
class CarModule { constructor() { let milesDriven = 0; let speed = 0; this.accelerate = (amount) => { speed = amount; milesDriven = speed; } this.getMilesDriven = () => milesDriven; } } const testCarModule = new CarModule(); testCarModule.accelerate(5); testCarModule.accelerate(4); console.log(testCarModule.getMilesDriven()); console.log(testCarModule.speed); // undefined -- We now have real variable privacy.
This approach implements true variable privacy because there is no direct access to any variable that is not intentionally disclosed. The problem is that we have now, well, not looking very good code compared to ours, and it also breaks the benefits of syntax sugar when we use classes. At this time, we might as well use function()
method.
Using WeakMap
There is also a more creative way to create private variables, which is to use WeakMap()
. While it may sound similar to Map
, the two are very different. While the mapping can take any type of value as a key, WeakMap
only accepts objects and deletes the values in WeakMap
when garbage collects object keys. Additionally, WeakMap
cannot iterate, meaning you have to access a reference to the object key to access the value. This makes it very useful for creating private variables, because variables are actually invisible.
class CarModule { constructor() { this.data = new WeakMap(); this.data.set(this, { milesDriven: 0, speed: 0 }); this.getMilesDriven = () => this.data.get(this).milesDriven; } accelerate(amount) { // In this version, we instead create a WeakMap and // use the "this" keyword as the key, which is unlikely // will be accidentally used as the key for the WeakMap. const data = this.data.get(this); const speed = data.speed amount; const milesDriven = data.milesDriven data.speed; this.data.set({ speed, milesDriven }); } } const testCarModule = new CarModule(); testCarModule.accelerate(5); testCarModule.accelerate(4); console.log(testCarModule.getMilesDriven()); console.log(testCarModule.data); //=> WeakMap { [items unknown] } -- This data cannot be easily accessed from the outside!
This solution is good at preventing accidental use of data, but it is not really private, as it can still be accessed from outside by replacing this
with CarModule
. Furthermore, it adds quite a bit of complexity and is therefore not the most elegant solution.
Use symbols to prevent conflict
If the purpose is to prevent name conflicts, you can use Symbol
to provide a useful solution. These are essentially instances that can act as unique values, and they will never equal any other value except its own unique instance. Here is an example of it in real-life applications:
class CarModule { constructor() { this.speedKey = Symbol("speedKey"); this.milesDrivenKey = Symbol("milesDrivenKey"); this[this.speedKey] = 0; this[this.milesDrivenKey] = 0; } accelerate(amount) { // This data is almost impossible to access unexpectedly. It is by no means private. // But it stays away from anyone who will implement this module. this[this.speedKey] = amount; this[this.milesDrivenKey] = this[this.speedKey]; } getMilesDriven() { return this[this.milesDrivenKey]; } } const testCarModule = new CarModule(); testCarModule.accelerate(5); testCarModule.accelerate(4); console.log(testCarModule.getMilesDriven()); console.log(testCarModule.speed); // => undefined -- We need to access the internal key to access the variable. Like the underscore solution, this approach relies more or less on naming conventions to prevent confusion.
TC39 Private Class Field Proposal
Recently, a new proposal was proposed that would introduce private variables to the class. It's simple: add # before the variable name and it becomes private. No additional structural changes are required.
class CarModule { #speed = 0 #milesDriven = 0 accelerate(amount) { // This data is almost impossible to access unexpectedly. this.#speed = amount; this.#milesDriven = speed; } getMilesDriven() { return this.#milesDriven; } } const testCarModule = new CarModule(); testCarModule.accelerate(5); testCarModule.accelerate(4); console.log(testCarModule.getMilesDriven()); console.log(testCarModule.speed); //=> undefined -- We need to access the internal key to access the variable.
Private class features have become a reality and have quite good browser support.
in conclusion
This is a summary of the various ways to implement private variables in JavaScript. There is no "correct" approach. These methods are suitable for different requirements, existing code bases, and other constraints. While each approach has its advantages and disadvantages, ultimately, all approaches are equally effective as long as they effectively solve your problem.
Thank you for reading! I hope this gives you some insight on how to apply scope and variable privacy to improve your JavaScript code. This is a powerful technology that supports many different methods and makes your code easier to use and error-free. Try some new examples yourself to get a better feeling.
The above is the detailed content of Implementing Private Variables In JavaScript. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics





It's out! Congrats to the Vue team for getting it done, I know it was a massive effort and a long time coming. All new docs, as well.

With the recent climb of Bitcoin’s price over 20k $USD, and to it recently breaking 30k, I thought it’s worth taking a deep dive back into creating Ethereum

I had someone write in with this very legit question. Lea just blogged about how you can get valid CSS properties themselves from the browser. That's like this.

I'd say "website" fits better than "mobile app" but I like this framing from Max Lynch:

The other day, I spotted this particularly lovely bit from Corey Ginnivan’s website where a collection of cards stack on top of one another as you scroll.

If we need to show documentation to the user directly in the WordPress editor, what is the best way to do it?

There are a number of these desktop apps where the goal is showing your site at different dimensions all at the same time. So you can, for example, be writing

Questions about purple slash areas in Flex layouts When using Flex layouts, you may encounter some confusing phenomena, such as in the developer tools (d...
