JavaScript’s object-oriented programming has evolved greatly, especially with the introduction of private properties. Private properties allow developers to hide internal details of an object, making sure that some data stays protected and cannot be accessed or modified from outside the object. This encapsulation concept is important for building robust applications, as it ensures that the internal state of an object cannot be accidentally changed or misused by external code.
In JavaScript, private properties differ from the usual public properties because they are not accessible directly on the object instance. Unlike public properties, which anyone can read or write, private properties remain hidden and can only be accessed or modified inside the object’s own methods. This article will guide you through how to create and use private properties in JavaScript with practical, fun examples using modern syntax and also older closure-based techniques.
Declaring Private Properties with the #
Syntax
The modern way to declare private properties in JavaScript classes uses the #
symbol before the property name. This syntax was introduced in recent ECMAScript versions and is now widely supported. Private properties declared this way are truly private — they cannot be accessed outside the class, even by mistake.
Let’s start with a simple class called SecretDiary
. It will have a private property #entries
that stores diary entries. The only way to interact with these entries will be through class methods.
class SecretDiary {
#entries = [];
addEntry(entry) {
this.#entries.push(entry);
}
getEntries() {
return [...this.#entries];
}
}
const myDiary = new SecretDiary();
myDiary.addEntry("Today I learned about private properties.");
myDiary.addEntry("JavaScript is fun!");
console.log(myDiary.getEntries());
In this example, #entries
is a private array. We add new diary entries using the addEntry
method, and getEntries
returns a copy of all entries. Notice we never access #entries
outside the class — trying to do so would cause an error.
This approach is clean, clear, and keeps the diary entries hidden from any direct outside access.
Accessing Private Properties inside Class Methods
Private properties are accessible only within the class body, typically inside methods. This means that methods like addEntry
and getEntries
can freely read and update the private #entries
array, but code outside cannot.
For example, if you try:
console.log(myDiary.#entries);
This will throw a syntax error, because JavaScript does not allow access to private properties from outside the class.
Thus, private properties create a controlled interface: only the class itself decides how to use or reveal the internal data, making your code more secure and easier to maintain.
Attempting to Access Private Properties Outside the Class
To demonstrate what happens when you try to peek into private properties from outside, consider this snippet:
try {
console.log(myDiary.#entries);
} catch (error) {
console.log("Error:", error.message);
}
When run, this throws an error: Private field '#entries' must be declared in an enclosing class
. This means JavaScript strictly enforces privacy, protecting your internal data from accidental or intentional external access.
Private Properties Using Closures (Alternative Approach)
Before the #
syntax was introduced, JavaScript developers used closures to create private properties. Closures allow you to hide variables inside a function scope, which external code cannot directly reach.
Here’s an example using a constructor function, creating a MagicBox
that stores secret items privately:
function MagicBox() {
let contents = [];
this.addItem = function(item) {
contents.push(item);
};
this.getContents = function() {
return [...contents];
};
}
const box = new MagicBox();
box.addItem("Ruby");
box.addItem("Emerald");
console.log(box.getContents());
In this example, the variable contents
exists only inside the MagicBox
function’s scope. It’s not accessible directly from the box
object, but the public methods addItem
and getContents
allow interaction with the private data.
Accessing and Modifying Private Properties via Public Methods
Notice in both the SecretDiary
and MagicBox
examples, private data is always manipulated through public methods. This encapsulation pattern helps prevent misuse and controls how the internal data changes over time.
For instance, you cannot accidentally overwrite the contents
of MagicBox
or the #entries
in SecretDiary
without using the designated methods.
Using Private Properties with Inheritance
Private properties declared with the #
syntax are truly private to the class they are defined in and are not accessible by subclasses. Let’s see what happens when a subclass tries to access private properties:
class SecretDiary {
#entries = [];
addEntry(entry) {
this.#entries.push(entry);
}
getEntries() {
return [...this.#entries];
}
}
class FriendDiary extends SecretDiary {
tryToReadEntries() {
try {
return this.#entries;
} catch (error) {
return "Cannot access private entries here!";
}
}
}
const friendDiary = new FriendDiary();
friendDiary.addEntry("Friend's secret!");
console.log(friendDiary.getEntries());
console.log(friendDiary.tryToReadEntries());
In this code, FriendDiary
tries to access #entries
directly and fails, because private fields are limited to the original class. However, FriendDiary
can still use public methods inherited from SecretDiary
to interact with the entries safely.
Combining Private Properties with Static Properties or Methods
Static methods belong to the class itself, not to any particular instance, so they cannot access private instance properties directly. For example:
class TreasureChest {
#goldCoins = 0;
constructor(coins) {
this.#goldCoins = coins;
}
getGold() {
return this.#goldCoins;
}
static totalGold(chests) {
return chests.reduce((sum, chest) => sum + chest.getGold(), 0);
}
}
const chest1 = new TreasureChest(100);
const chest2 = new TreasureChest(200);
console.log(TreasureChest.totalGold([chest1, chest2]));
Here, the static method totalGold
calculates the total gold from multiple instances by calling their public getGold
methods, since it cannot access the private #goldCoins
directly.
Real-World Fun Example: TreasureChest
To wrap up the concepts, let’s build a fun TreasureChest
class that uses private properties to keep its treasures hidden. It will have private #goldCoins
and #gems
, and methods to add or remove treasure, plus a method to display the treasure count without revealing the exact details.
class TreasureChest {
#goldCoins = 0;
#gems = 0;
constructor(gold, gems) {
this.#goldCoins = gold;
this.#gems = gems;
}
addGold(amount) {
this.#goldCoins += amount;
}
addGems(amount) {
this.#gems += amount;
}
openChest() {
return `You have ${this.#goldCoins} gold coins and ${this.#gems} gems inside!`;
}
}
const chest = new TreasureChest(50, 10);
chest.addGold(25);
chest.addGems(5);
console.log(chest.openChest());
In this example, the #goldCoins
and #gems
cannot be accessed from outside, but the openChest
method gives a friendly summary of the treasure inside.
Conclusion
Private properties in JavaScript allow objects to keep internal data hidden from outside access, providing a strong encapsulation tool for cleaner and safer code. The modern #
syntax lets you declare true private fields within classes that can only be accessed by the class’s own methods. Alternatively, closures can be used in function constructors to simulate private properties by leveraging function scope.
Through the examples of SecretDiary
, MagicBox
, and TreasureChest
, we explored how to declare, access, and protect private data. We also saw how private properties behave with inheritance and static methods.
Mastering private properties helps you write more secure and maintainable JavaScript code, making your classes smarter and your data safer.
References
If you want to learn more about JavaScript private properties and related concepts, these resources are excellent starting points:
- MDN Web Docs: Private class fields
The official guide to using private fields in JavaScript classes. - MDN Web Docs: Closures
Understand how closures work and how they can be used to create private variables. - ECMAScript Proposal: Class Fields
Details on the syntax and proposals around private and public class fields. - JavaScript.info: Class Fields
Clear explanations and examples of private class fields.