JavaScript private fields let you keep class data accessible only from within the class that declares it. A private field starts with the hash (#) character, such as #name. Unlike an ordinary property, it cannot be read or changed directly from outside the class. Private fields help protect an object’s internal state, prevent accidental changes, and make class APIs easier to maintain.
What Are Private Fields in JavaScript?
A private field is a class element declared with a # prefix. It belongs to the class instance or class constructor, depending on whether it is an instance or static private field. JavaScript enforces its privacy through the language itself, rather than relying on a naming convention.
Private class elements were standardized in modern JavaScript as part of the class fields features. Public and private instance fields are widely supported in modern runtimes. When targeting older browsers or JavaScript engines, check your build tools and runtime compatibility.
Basic Syntax of JavaScript Private Fields
Declare a private field inside the class body by placing # before its name. You can initialize it where it is declared or assign its value inside the constructor.
class Person {
#name = "Nadia";
getName() {
return this.#name;
}
}
const person1 = new Person();
console.log(person1.getName()); // "Nadia"
// This is not allowed outside the class:
// console.log(person1.#name);
In this example, #name is private to the Person class. The public getName() method provides controlled access to its value. Trying to access person1.#name outside the class body is a syntax error.
Declaring Private Fields in a Constructor
You can declare a private field without an initial value and assign it in the constructor. The field must still be declared in the class body before you use it.
class Employee {
#salary;
constructor(name, salary) {
this.name = name;
this.#salary = salary;
}
getSalary() {
return this.#salary;
}
}
const employee1 = new Employee("Rahim", 50000);
console.log(employee1.name); // "Rahim"
console.log(employee1.getSalary()); // 50000
The name property is public, while #salary is private. Code outside the class can read employee1.name, but it must use a public method or accessor to get the salary.
Using Private Fields with Getters and Setters
Private fields are often combined with public getters and setters. This gives outside code a familiar property interface while allowing the class to validate or format values before they are read or updated.
class Account {
#balance = 0;
get balance() {
return this.#balance;
}
set balance(value) {
if (typeof value !== "number" || value < 0) {
throw new TypeError("Balance must be a non-negative number.");
}
this.#balance = value;
}
}
const account1 = new Account();
account1.balance = 1500;
console.log(account1.balance); // 1500
The getter returns the private #balance value, and the setter checks the new value before storing it. Outside code uses account1.balance, but it never accesses the private field directly.
Private Methods in JavaScript
JavaScript also supports private methods. A private method uses the same # prefix and can only be called from within the class that declares it. Private methods are useful for internal operations that should not be part of the public API.
class Calculator {
#multiply(a, b) {
return a * b;
}
calculateSquare(number) {
return this.#multiply(number, number);
}
}
const calculator = new Calculator();
console.log(calculator.calculateSquare(5)); // 25
// Not allowed:
// calculator.#multiply(5, 5);
The #multiply() method is an internal helper. The public calculateSquare() method uses it, but code outside the class cannot call the private method directly.
Private Static Fields in JavaScript
A private static field belongs to the class itself instead of to each instance. Declare it using static followed by the private name. It can be accessed only from within the class body that declares it.
class User {
static #userCount = 0;
constructor(name) {
this.name = name;
User.#userCount++;
}
static getUserCount() {
return User.#userCount;
}
}
const user1 = new User("Ava");
const user2 = new User("Nadia");
console.log(User.getUserCount()); // 2
// Not allowed outside the class:
// console.log(User.#userCount);
The #userCount field is shared by the User class, not stored separately on each user object. The public static method provides a controlled way to read it.
Private Static Methods
You can also declare private static methods. These methods are internal helpers that belong to the class constructor rather than to individual instances.
class Validator {
static #isValidEmail(value) {
return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(value);
}
static checkEmail(value) {
return this.#isValidEmail(value);
}
}
console.log(Validator.checkEmail("ava@example.com")); // true
console.log(Validator.checkEmail("not-an-email")); // false
The private static method #isValidEmail() performs the validation. The public static method checkEmail() exposes the result without making the internal helper available to outside code.
Checking Private Fields with the in Operator
JavaScript provides a special use of the in operator for private fields. Inside the class that declares a private field, you can write #field in object to check whether that object has the private field. This is useful for validating that an object has the correct private-field brand before accessing it.
class Person {
#name;
constructor(name) {
this.#name = name;
}
static hasPersonName(object) {
return #name in object;
}
}
const person1 = new Person("Ava");
const regularObject = {};
console.log(Person.hasPersonName(person1)); // true
console.log(Person.hasPersonName(regularObject)); // false
The private name #name can be used in this check because the code is inside the class that declares it. Outside the class, the private name is not available for use in an in expression.
Private Fields and JavaScript Inheritance
Private fields are not accessible by name from a child class. A subclass cannot directly read or write a private field declared by its parent, even though it inherits the parent’s public and protected-through-API behavior. The parent class can provide public or protected-style methods when subclasses need controlled access to internal data.
class Person {
#name;
constructor(name) {
this.#name = name;
}
getName() {
return this.#name;
}
}
class Student extends Person {
introduce() {
return "My name is " + this.getName();
}
}
const student1 = new Student("Rahim");
console.log(student1.introduce()); // "My name is Rahim"
// A subclass cannot do this:
// this.#name
The Student class cannot refer directly to #name because that private name belongs to Person. It can use the inherited public getName() method instead. This keeps the parent’s internal representation under the parent’s control.
Private Fields vs. Underscore-Prefixed Properties
Before JavaScript had native private fields, developers often used an underscore prefix, such as _salary, to indicate that a property was intended for internal use. The underscore is only a naming convention: it does not prevent outside code from reading or changing the property.
class OldAccount {
constructor() {
this._balance = 100;
}
}
const oldAccount = new OldAccount();
oldAccount._balance = 0; // Allowed
console.log(oldAccount._balance); // 0
class SecureAccount {
#balance = 100;
getBalance() {
return this.#balance;
}
}
const secureAccount = new SecureAccount();
console.log(secureAccount.getBalance()); // 100
// Not allowed:
// secureAccount.#balance = 0;
The underscore-prefixed property is still a normal public property. A hash-prefixed private field is enforced by JavaScript, and attempts to access it outside the declaring class are rejected.
Private Fields Are Not Ordinary Object Properties
Private fields are not ordinary string-keyed properties. They do not appear in Object.keys(), Object.getOwnPropertyNames(), or JSON serialization. They are also not accessible through bracket notation or reflection APIs designed for ordinary properties.
class User {
#password = "secret";
constructor(name) {
this.name = name;
}
}
const user = new User("Ava");
console.log(Object.keys(user)); // ["name"]
console.log(JSON.stringify(user)); // {"name":"Ava"}
The public name property appears in the results, but the private #password field does not. This behavior is part of the language’s private-field model; it is not simply a hidden property name.
