Functions
Prefer arrow over traditional function expression
Arrow functions are a more concise and straightforward way of defining functions in JavaScript, and are generally preferred to traditional function declarations because they are easier to read,
do not have their own this
binding, and have values that are determined by the enclosing context rather than how the function is invoked or defined. This can make arrow functions simpler to work with and less prone to errors.
// 🚫 Bad
function add(x, y) {
return x + y;
}
// ✅ Good
const add = (x, y) => x + y;
Keep functions small and focused
Keeping functions small and focused helps to improve the readability, maintainability, and testability of your code. Smaller functions are easier to understand, modify, and reuse, and are less prone to errors. By keeping functions focused on a single, well-defined task, you can reduce the complexity of your code and make it easier to reason about and work with.
const fetchUser = async (id) => {
const response = await fetch(`https://api.example.com/users/${id}`);
const user = await response.json();
return user;
};
const updateUser = async (id, updates) => {
const response = await fetch(`https://api.example.com/users/${id}`, {
method: "PATCH",
body: JSON.stringify(updates),
});
const user = await response.json();
return user;
};
Avoid multiple arguments
Using multiple arguments in a function can make it more difficult to refactor or modify functions, or lead to unintended consequences. Instead, use a single object argument that contains all of the input values that are needed. This can make the function more flexible and easier to work with, as the object can be easily modified or extended without affecting the function's signature.
// 🚫 Bad
const add = (x, y, z) => x + y + z;
// ✅ Good
const add = ({ x, y, z }) => x + y + z;
Avoid multiple returns
Multiple returns in a function can make the code harder to read and understand, as it is not immediately clear when or why each return statement is being executed.
// 🚫 Bad
const getStatus = (isActive) => {
if (isActive) {
return "Active";
} else {
return "Inactive";
}
};
// ✅ Good
const getStatus = (isActive) => (isActive ? "Active" : "Inactive");
Avoid the arguments object
The arguments
object is not a true array and does not have access to all of the array methods, which can make it more difficult to work with.
Instead, use the rest
operator to create an array of the arguments.
// 🚫 Bad
const sum = () => {
let total = 0;
for (let i = 0; i < arguments.length; i++) {
total += arguments[i];
}
return total;
};
// ✅ Good
const sum = (...numbers) =>
numbers.reduce((total, current) => total + current, 0);
Avoid the apply method
A variadic function is a function that can accept a variable number of arguments.
The spread operator is a better choice for calling variadic functions because it is more concise, easier to read, contextless, easier to compose, and more widely supported than the apply
method.
const sum = (...numbers) =>
numbers.reduce((total, current) => total + current, 0);
const numbers = [1, 2, 3, 4, 5];
// 🚫 Bad - Using apply method
console.log(sum.apply(null, numbers)); // 15
// ✅ Good - Using spread operator
console.log(sum(...numbers)); // 15
Use default parameters and values
Using default parameters and values allows you to specify default values for function arguments, which can make the function more resilient and easier to use, as it allows the caller to omit certain arguments or pass in different values as needed. This can help to improve the readability and maintainability of the function, as it reduces the need for complex conditional logic or error handling.
// 🚫 Bad
const greet = (name) => `Hello, ${name}!`;
console.log(greet("John")); // "Hello, John!"
console.log(greet()); // Uncaught TypeError: Cannot read property 'name' of undefined
// ✅ Good - Using default parameter
const greet = (name = "world") => `Hello, ${name}!`;
console.log(greet("John")); // "Hello, John!"
console.log(greet()); // "Hello, world!"
Don't do dynamic code evaluation
Avoiding dynamic code evaluation can improve the security and reliability of the code, as it reduces the risk of injection attacks and other malicious input that could compromise the system.
let code = 'console.log("Hello, World!")';
eval(code); // logs "Hello, World!" to the console