Stateless or Stateful? Why Static Methods and Stateless best friends in JavaScript.
The ability to tell if a object is stateless or stateful is a big plus when writing your code. Stateful means the object needs to remember something across calls, while Stateless means each call is independent, no memory of the past required. Since stateless means take input, return output, and forget about it then math operations, date formatting, string helpers and file type detection are good candidates for stateless. Consequently, stateful would mean take input, change or depend on some remembered value, and let future calls be affected by the past thus counter, shopping cart, cache event emitters are stateful.
How do we tell if a class should be stateful or stateless
If the object remembers, has side effects (mutates), has Input / output inconsistency and needs an instance we can say its Stateful otherwise it is stateless.
We will ask ourselves FOUR questions to answer if a class is stateful or stateless.
Does it remember anything between calls?
- Stateless: Here every call stands alone thus nothing is remembered.js
Math.max(3, 7); // Always returns 7, no memory involved - Stateful: It changes behavior based on previous calls.js
let count = 0; function nextId() { return ++count; } // Returns different values depending on history
- Stateless: Here every call stands alone thus nothing is remembered.
Does it modify hidden data (side effects)?
- Stateless: Pure function — doesn’t touch outside variables, just works on inputs.
jsstr => str.toUpperCase(); // No side effect- Stateful: Mutates state outside itself.js
let users = []; function addUser(u) { users.push(u); } // Side effect on global state
Can I replace it with a lookup table?
- Stateless: If the same input always gives the same output, it’s stateless.js
f(5) → always 25 (for x*x) - Stateful: If the result depends on time/history, a lookup won’t work.js
Date.now() → Different every call
- Stateless: If the same input always gives the same output, it’s stateless.
Do I need new (an instance) to use it?
- Stateless: Usually no — static methods or plain functions are fine.js
Validator.isEmail("test@example.com"); - Stateful: Needs an object to keep track of context (state).js
const cart = new ShoppingCart(); cart.addItem("apple"); // Cart remembers items
- Stateless: Usually no — static methods or plain functions are fine.
For stateless objects in Javascript static methods are a big deal.
Here we will create a Validator class which is stateless and apply static methods on it to perform our validation logic. Note checking if a string is an email doesn’t depend on history also, validating a password is the same every time and rules don’t need this.items or this.state. Lets check the below code.
class Validator {
static isEmail(str) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(str);
}
static isPhone(str) {
return /^\+?[1-9]\d{7,14}$/.test(str);
}
static isStrongPassword(str) {
return (
str.length >= 8 &&
/[A-Z]/.test(str) &&
/[a-z]/.test(str) &&
/[0-9]/.test(str) &&
/[\W_]/.test(str)
);
}
static isRequired(value) {
return value !== null && value !== undefined && value !== "";
}
}
// usage
if (!Validator.isEmail("john@example.com")) {
throw new Error("Invalid email");
}else{
console.log('valid email') //valid email
}advantages
Discoverable: On IDEs autocomplete will show all available validators. Declarative: Business intent is obvious from the method names. Maintainable: One class, one responsibility. Lightweight: No unnecessary dependencies. Scalable
We said scalable? Lets scale then
Here we will express rules declaratively thus, add this validate method
static validate(schema, data) {
for (const [field, rules] of Object.entries(schema)) {
for (const rule of rules) {
if (!rule(data[field])) {
throw new Error(`Invalid ${field}`);
}
}
}
return true;
}thus we can validate the schema this way;
const schema = {
email: [Validator.isRequired, Validator.isEmail],
password: [Validator.isRequired, Validator.isStrongPassword],
};
Validator.validate(schema, {
email: "john@example.com",
password: "Secret123!",
});::: The schema is; Composable - rules are reusable functions. Declarative - schema reads like documentation. Extensible - adding a new rule is a one-liner. :::
javascript book
If this interested you, check out my Javascript Book