Best practices for variable assignment, naming, and function declarations.
const over let let only when a variable
will be reassigned
var// ✅ Preferred - use const by default
const userName = 'Sebastian';
const userAge = 28;
const userPreferences = {
theme: 'dark',
language: 'en',
notifications: true
};
// ✅ Use let when reassignment is needed
let currentStep = 1;
let isLoading = false;
function processSteps() {
for (let i = 0; i < steps.length; i++) {
currentStep = i + 1; // Reassignment needed
processStep(steps[i]);
}
isLoading = true; // State change
}
// ✅ Modifying object properties is allowed with const
const user = { name: 'Alice', age: 30 };
user.age = 31; // This is fine - we're not reassigning 'user'
user.email = '[email protected]'; // Adding properties is also fine
// ❌ Avoid var - has function scope and hoisting issues
var count = 0; // Don't do this
if (true) {
var message = 'Hello'; // This is hoisted and can cause confusion
}
// ❌ Don't use let when const would work
let API_URL = 'https://api.example.com'; // Should be const
let processedData = transformData(rawData); // Should be const if not reassigned // ✅ Descriptive variable names
const userEmailAddress = '[email protected]';
const shoppingCartItems = [];
const hasActiveSubscription = true;
const isEmailVerified = false;
const canEditProfile = user.role === 'admin';
const shouldShowWelcomeMessage = isFirstVisit && !hasSeenWelcome;
// ✅ Acceptable single-letter variables in clear contexts
const numbers = [1, 2, 3, 4, 5];
const doubledNumbers = numbers.map(n => n * 2);
const filteredUsers = users.filter(u => u.isActive);
// ✅ Good function parameter names
function calculateShippingCost(orderTotal, shippingDistance, isPriorityDelivery) {
// Implementation
}
// ❌ Poor variable names
const e = '[email protected]'; // What is 'e'?
const addr = getUserAddress(); // Abbreviation unclear
const flag = true; // What does this flag represent?
const data = fetchUserInfo(); // Too generic
const temp = calculateTotal(); // Temporary for what?
// ❌ Boolean variables that don't ask questions
const subscription = true; // Should be 'hasSubscription'
const email = false; // Should be 'isEmailVerified' or 'hasEmail' ===)
==) unless absolutely necessary
// ✅ Strict equality - always preferred
const userAge = 25;
const inputAge = '25';
if (userAge === parseInt(inputAge, 10)) {
console.log('Ages match');
}
// ✅ Explicit type checking
if (typeof value === 'string' && value.length > 0) {
processString(value);
}
// ✅ Explicit null/undefined checks
if (user.email !== null && user.email !== undefined) {
sendEmail(user.email);
}
// ✅ Using nullish coalescing operator
const displayName = user.fullName ?? user.username ?? 'Anonymous';
// ✅ Checking for array length
if (items.length === 0) {
showEmptyState();
}
// ❌ Loose equality - can cause unexpected behavior
if (userAge == inputAge) { // '25' == 25 is true, but types differ
console.log('This might not be what you expect');
}
// ❌ Truthy/falsy when you need exact values
if (items.length) { // What if length is exactly what we're checking?
// This fails when length is 0, but 0 might be a valid value
}
// ❌ Implicit type coercion
if (userInput) { // What if userInput is '0' or false intentionally?
processInput(userInput);
} // These all return true with == (but false with ===)
0 == false
'' == false
null == undefined
'0' == false
[] == false
// Always use === to avoid these surprises
0 === false // false
'' === false // false
null === undefined // false // ✅ Function declarations for named functions
function calculateTax(amount, rate) {
return amount * rate;
}
function processUser(user) {
validateUser(user);
saveUser(user);
sendWelcomeEmail(user);
}
// ✅ Arrow functions for short, anonymous functions
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(n => n * 2);
const evens = numbers.filter(n => n % 2 === 0);
// ✅ Arrow functions for callbacks
button.addEventListener('click', () => {
toggleMenu();
});
// ✅ Object method shorthand
const userService = {
async createUser(userData) {
const user = await this.validateUserData(userData);
return await this.saveToDatabase(user);
},
validateUserData(data) {
// Validation logic
return data;
}
};
// ✅ Named arrow functions for better debugging
const calculateDiscount = (price, percentage) => {
return price * (percentage / 100);
};
// ❌ Avoid anonymous functions when debugging is important
users.forEach(function(user) { // Hard to identify in stack traces
processUser(user);
});
// ✅ Better - named function
users.forEach(function processUserCallback(user) {
processUser(user);
}); // ✅ Default parameters
function greetUser(name = 'Guest', greeting = 'Hello') {
return `${greeting}, ${name}!`;
}
// ✅ Object destructuring for parameters
function createUser({ name, email, age = 18, isAdmin = false }) {
return {
name,
email,
age,
isAdmin,
createdAt: new Date()
};
}
// ✅ Usage with object parameters
const newUser = createUser({
name: 'Alice',
email: '[email protected]',
age: 25
});
// ✅ Rest parameters
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
// ✅ Combining destructuring with rest parameters
function processApiResponse({ data, status, ...metadata }) {
console.log('Status:', status);
console.log('Data:', data);
console.log('Metadata:', metadata);
}
// ❌ Too many individual parameters
function createUser(name, email, age, isAdmin, createdDate, lastLogin, preferences, settings) {
// Hard to remember parameter order and meaning
}
// ❌ Using arguments object
function sum() {
let total = 0;
for (let i = 0; i < arguments.length; i++) {
total += arguments[i]; // arguments is not a real array
}
return total;
}