Modern ES6+ Classes
While JavaScript remains prototypal under the hood, Classesprovide a structured, readable syntax for implementing Object-Oriented patterns. Modern updates (ES2022) have added true private fields and static initialization blocks, making them production-ready for enterprise architecture.
Encapsulation and Fields
Modern JavaScript classes support Public andPrivate fields. Unlike the old _variableconvention, fields prefixed with # are truly private at the engine level and cannot be accessed from outside the class body, providing real data integrity.
class ComputeEngine {
// 1. Public Fields (ES2022)
region = 'us-east-1';
// 2. Private Fields (#) - Truly encapsulated
#status = 'OFFLINE';
constructor(id) {
this.id = id;
}
// 3. Instance Method
start() {
this.#status = 'RUNNING';
console.log(`Engine ${this.id} is ${this.#status}`);
}
// 4. Getter/Setter
get status() { return this.#status; }
}Static Initialization
Static methods and properties belong to the class itself, not its instances. ES2022 introduced Static Blocks, allowing for complex logic (like try/catch) when the class is first loaded by the engine.
class Database {
static connectionLimit = 100;
// ES2022 Static Initialization Block
static {
// Complex logic to run once when class is loaded
try {
const config = { env: 'PROD' };
this.mode = config.env === 'PROD' ? 'SECURE' : 'DEV';
} catch (e) {
this.mode = 'SAFE';
}
}
static connect() {
console.log(`Connecting in ${this.mode} mode...`);
}
}The Super Mechanism
Inheritance allows a class to share behavior from a parent. The extends keyword sets up the prototype chain, and super() calls the parent constructor. Crucially, you cannot use this in a child constructor untilsuper() has been called.
class BaseAPI {
constructor(endpoint) {
this.endpoint = endpoint;
}
fetch() { console.log('Base Fetch'); }
}
class UserAPI extends BaseAPI {
constructor(endpoint, token) {
// Must call super() before accessing 'this'
super(endpoint);
this.token = token;
}
// Overriding while calling parent
fetch() {
super.fetch();
console.log('User Auth Header Applied');
}
}Performance Considerations
Under the hood, class methods are added to the .prototype. This makes them highly memory efficient as thousands of instances can share a single function reference. However, using arrow functions as fields creates a new function for every instance.
// --- Performance Perspective ---
class HeavyObject {
// ⌠Anti-pattern: Creating methods inside constructor
constructor() {
this.log = () => console.log('Inefficient');
}
// ✅ Best practice: Method on prototype
log() {
console.log('Efficient');
}
}
/*
Note: Field initializers (like click = () => {})
are added to the instance, not the prototype.
Use them only when context binding is critical (e.g., React handlers).
*/this context for event listeners.Senior Engineer's Checklist:
- ✅ Encapsulation: Use
#privatefields for security-sensitive data. - ✅ Inheritance: Use
superto extend parent behaviors rather than rewriting them. - ✅ Static Blocks: Use static blocks for complex configuration validation on startup.
- ⌠Hoisting: Remember that classes are not hoisted; you must define them before usage.
- ✅ Strictness: Class bodies are always in
"use strict"mode by default.