How to Write Readable JavaScript Code: Naming, Branches, and Functions
This article explains why readable code matters for developers, then offers practical guidelines on naming variables and functions, structuring conditional branches, keeping functions single‑purpose, handling errors, avoiding side‑effects, and organizing class methods to improve maintainability in modern JavaScript projects.
Writing readable code is crucial for programmers because the primary purpose of code is to be read, followed by correct execution. Unreadable but functional code increases maintenance cost, reduces stability, and harms team cohesion.
The author shares personal experiences on how to improve code readability, focusing on ES6/7 features that aid this goal.
JavaScript is dynamic and weakly typed, which made “quick and casual” coding acceptable in the IE6 era, but as front‑end projects grow, that habit becomes a major obstacle. This article discusses ES6/7 code because these versions have largely replaced classic JavaScript and provide features that improve readability.
Variable Naming
Variable names are the foundation of readable code. Names must convey enough information. For example, getData is vague, while fetchUserInfoAsync clearly indicates an asynchronous operation that returns a Promise.
Naming Basics
Usually nouns name objects and verbs name functions. Example:
monkey.eat(banana); // the monkey eats a banana
const apple = pick(tree); // pick an apple from the treeUsing plural nouns for collections (e.g., bananas for an array of banana) or suffixes like List or Map makes intent explicit.
Some words have identical singular and plural forms, and uncountable nouns like data or information lack a plural; in those cases a suffix such as List can be used to denote a collection.
Naming in Context
Names should fit the surrounding scope. In a zoo‑management example, a function feedAnimals processes different food items depending on the current context, so variables like banana or peach make the logic clear.
function feedAnimals(food, animals) {
// ...
const banana = bananas.pop();
if (banana) {
monkey.eat(banana);
} else {
const peach = peaches.pop();
monkey.eat(peach);
}
}The function’s purpose is obvious: feed all animals with available food, and the variable names reflect that purpose.
Strict Naming Conventions
Adopting a consistent naming rule eliminates the need to remember multiple variants. For instance, always using fetchUserInformation instead of alternating with getUserData makes the code self‑explanatory.
Branch Structures
Well‑structured branches resemble a binary tree, making mental simulation easier. However, many developers write confusing nested or combined conditions.
Bad Practice: Return Inside a Branch
function foo() {
if (condition) {
// branch 1 logic
return;
}
// branch 2 logic
}Placing the main logic after an early return hides execution flow. Moving the secondary logic into an else block clarifies intent.
function foo() {
if (condition) {
// branch 1 logic
} else {
// branch 2 logic
}
}If a branch is intentionally empty, leave an empty line to signal that no action is required.
Bad Practice: Combining Multiple Conditions
if (cond1 && cond2 && cond3) {
// main logic
} else {
// secondary logic
}When several conditions can fail independently, the combined form obscures which specific condition leads to the secondary path. Splitting them into nested if‑else statements makes each case explicit.
if (cond1) {
if (cond2) {
// main logic
} else {
// branch 2 logic
}
} else {
// branch 3 logic
}Bad Practice: Using Branches to Mutate State
let foo = someValue;
if (condition) {
foo = doSomethingToFoo(foo);
}
// continue using fooSuch patches accumulate and degrade readability. Prefer immutable patterns or explicit transformations, e.g., using a ternary expression.
const foo = condition ? doSomethingToFoo(someValue) : someValue;Functions
One Function, One Responsibility
A function that handles both single‑user and multiple‑user requests ( fetchUserInfo) forces readers to infer hidden rules. Splitting it into fetchSingleUser and fetchMultipleUser clarifies intent and eases maintenance.
async function fetchMultipleUser(idList) {
return await request.post('/api/users/', {idList});
}
async function fetchSingleUser(id) {
return await fetchMultipleUser([id])[0];
}Proper Error Handling
Silently swallowing errors (e.g., using || or &&) hides failures. Instead, propagate errors or handle them explicitly, allowing callers to decide how to react.
const result = await fetchUserFavorites(id);
if (result.success) {
user.favoriteBooks = result.data.books;
} else {
user.favoriteBooks = [];
}
// or simply let the exception bubble up
user.favoriteBooks = (await fetchUserFavorites(id)).books;Controlling Side Effects
Pure functions avoid hidden side effects. A function that mutates a passed‑in object (e.g., addFavoritesToUser) makes the caller unaware of the changes. Returning a new object or a structured result keeps the contract clear.
async function getUserDetail(id) {
const user = await fetchSingleUserInfo(id);
const {books, songs, isMusicFan} = await getUserFavorites(id);
return Object.assign(user, {books, songs, isMusicFan});
}
async function getUserFavorites(id) {
const {books, songs} = await fetchUserFavorites(id);
return {books, songs, isMusicFan: songs.length > 100};
}Non‑Intrusive Refactoring
When adding caching to fetchUserInfo, wrapping the original function with a decorator ( memorizeThunk) preserves readability and isolates the new concern.
const memorizeThunk = (func, reducer) => {
const cache = {};
return (...args, callback) => {
const key = reducer(...args);
if (cache[key]) {
callback(...cache[key]);
} else {
func(...args, (...result) => {
cache[key] = result;
callback(...result);
});
}
};
};
const fetchUserInfoCache = memorizeThunk(fetchUserInfo, userId => userId);Class Structure
Avoid Overusing Member Methods
Helper functions that are only used inside render (e.g., chunk for grouping users) should be defined locally rather than as class members, preventing unnecessary exposure and reducing cognitive load for readers.
Conclusion
Just as writers refine prose, developers should periodically review and refactor code for readability. By paying attention to naming, branch clarity, single‑purpose functions, error handling, side‑effect control, and thoughtful class design, we develop a “code sense” that leads to higher‑quality, maintainable software.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Taobao Frontend Technology
The frontend landscape is constantly evolving, with rapid innovations across familiar languages. Like us, your understanding of the frontend is continually refreshed. Join us on Taobao, a vibrant, all‑encompassing platform, to uncover limitless potential.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
