How to use WeakMap for private data in JavaScript
WeakMap provides true private data storage in JavaScript without memory leaks, as entries are automatically garbage collected when objects are no longer referenced. As the creator of CoreUI with 26 years of JavaScript development experience, I’ve used WeakMap for private instance data in production libraries serving millions of users.
The most effective approach stores private data in a WeakMap keyed by the object instance.
Direct Answer
Use WeakMap to store private instance data:
const privateData = new WeakMap()
class User {
constructor(name, password) {
privateData.set(this, { password })
this.name = name
}
checkPassword(input) {
return privateData.get(this).password === input
}
}
const user = new User('John', 'secret123')
console.log(user.name) // 'John'
console.log(user.password) // undefined
console.log(user.checkPassword('secret123')) // true
Multiple Private Properties
const privateProps = new WeakMap()
class BankAccount {
constructor(owner, balance) {
privateProps.set(this, {
balance,
transactions: []
})
this.owner = owner
}
deposit(amount) {
const data = privateProps.get(this)
data.balance += amount
data.transactions.push({ type: 'deposit', amount })
}
withdraw(amount) {
const data = privateProps.get(this)
if (data.balance >= amount) {
data.balance -= amount
data.transactions.push({ type: 'withdraw', amount })
return true
}
return false
}
getBalance() {
return privateProps.get(this).balance
}
}
Private Methods
const privateMethods = new WeakMap()
class Calculator {
constructor() {
privateMethods.set(this, {
validate: (num) => {
if (typeof num !== 'number') {
throw new Error('Not a number')
}
}
})
}
add(a, b) {
const methods = privateMethods.get(this)
methods.validate(a)
methods.validate(b)
return a + b
}
}
Factory Pattern
const createCounter = () => {
const privateData = new WeakMap()
class Counter {
constructor(initial = 0) {
privateData.set(this, { count: initial })
}
increment() {
const data = privateData.get(this)
data.count++
}
decrement() {
const data = privateData.get(this)
data.count--
}
getValue() {
return privateData.get(this).count
}
}
return Counter
}
const Counter = createCounter()
const counter = new Counter(10)
counter.increment()
console.log(counter.getValue()) // 11
Why WeakMap vs Symbol
// Symbol approach (not truly private)
const _password = Symbol('password')
class User {
constructor(password) {
this[_password] = password
}
}
const user = new User('secret')
console.log(Object.getOwnPropertySymbols(user)) // Can still access!
// WeakMap approach (truly private)
const privateData = new WeakMap()
class SecureUser {
constructor(password) {
privateData.set(this, { password })
}
}
const secureUser = new SecureUser('secret')
// No way to access password from outside!
Memory Management
const privateData = new WeakMap()
class Component {
constructor(id) {
privateData.set(this, { id, data: new Array(1000000) })
}
}
let component = new Component(1)
console.log(privateData.has(component)) // true
component = null
// Private data is automatically garbage collected!
Best Practice Note
This is the same WeakMap pattern we use in CoreUI for private component state without memory leaks. Unlike regular objects or Maps, WeakMap entries are automatically garbage collected when the key object is no longer referenced, preventing memory leaks in long-running applications.
Related Articles
For related encapsulation patterns, check out how to implement module pattern in JavaScript and how to create immutable objects in JavaScript.



