Explain Codes LogoExplain Codes Logo

Static variables in JavaScript

javascript
static-variables
es6-classes
closure
Nikita BarsukovbyNikita Barsukov·Jan 7, 2025
TLDR

Handle static variables in JavaScript using closures or associating properties to constructor functions. Here's a simple closure illustration:

function createCounter() { let count = 0; return () => count++; } const counter = createCounter(); console.log(counter()); // Outputs: 0 console.log(counter()); // Outputs: 1

In this case, createCounter function wraps up the count variable, securing its state between calls through the returned inner function.

Employing ES6 classes for static variables

ES6 classes offer a straightforward way to define static variables using the static keyword. These variables are directly connected to the class constructor, not the instances.

class MyClass { static myStaticValue = 0; static increment() { // Increment, rinse, repeat! this.myStaticValue++; } } MyClass.increment(); console.log(MyClass.myStaticValue); // Outputs: 1

Just like that, a single static variable serves all instances.

Safeguarding static variables via closures

A closure can create private static variables that remain shielded from the exterior, achieving an encapsulation we all crave for.

let Library = (function () { let bookCount = 0; // Private static variable return class { static incrementBooks() { return ++bookCount; // One more book in the library! } } })(); console.log(Library.incrementBooks()); // Outputs: 1

Inside this cocooned environment, bookCount stays private and can only be tweaked through privileged methods.

Prototype-powered shared methods

Consider using the prototype property to define methods that need not be reproduced across instances. This is memory-efficient and cuts down overhead stuff.

function Animal() {} Animal.prototype.speak = function () { // I'm a polyglot animal! return 'This animal makes a sound.'; }; let dog = new Animal(); console.log(dog.speak()); // Outputs: This animal makes a sound.

Every Animal instance shares the same speak method, loaded once in memory to save space.

Access management using Object.defineProperty

When you need to have absolute control, use Object.defineProperty to establish getter/setter functions.

class Example { // Static private variable static #secretValue = 123; static get secretValue() { // Our little secret! return Example.#secretValue; } static set secretValue(val) { Example.#secretValue = val; } } console.log(Example.secretValue); // Outputs: 123 Example.secretValue = 456; console.log(Example.secretValue); // Outputs: 456

Modules to the rescue!

The module pattern prevents the global scope from pollution. This pattern conceals the static variable in a self-invoking function to maintain its independence.

var MyApp = (function() { var privateStaticVar = 0; // Our little secret return { increment: function() { return ++privateStaticVar; // Level up! }, value: function() { return privateStaticVar; // Show me the value! } }; })(); console.log(MyApp.increment()); // Outputs: 1 console.log(MyApp.value()); // Outputs: 1

Embracing TypeScript

Consider TypeScript for a more traditional approach to static variables. It discloses access modifiers and offers a clearer syntax for declaring static members.

class MyClass { private static count = 0; public static increment() { this.count++; // Here's to progress! } } MyClass.increment(); console.log(MyClass.count); // Error: Property 'count' is private and only accessible within class 'MyClass'.

Dealing with anonymous statics

When dealing with anonymous functions, employ arguments.callee as a reference to maintain a static variable within the function itself.

let anonymousCounter = (function() { let count = 0; return function() { count++; console.log(count, arguments.callee); // Prints the function. Magic? No, JavaScript! }; })(); anonymousCounter(); // Outputs: 1, [Function]