In this article I will take you through “The good habits” when coding JavaScript.
1 — Avoid using new Object()
In JavaScript, using new Object is a bit risky, while using primitives always better for several reasons. Let’s dig deeper into this.
General speaking
The "" for example, creates a string primitive as we all know, on the other hand… new String() creates a string object. As they are more complex and have methods, string object can bring unexpected behavior, precisely when it comes to comparisons and type coercion.
Simplicity
Primitives are simpler in usage and more straightforward, as their usage avoids unnecessary complexity, and the code become easy to read and maintain.
Performance
Primitives are more efficient in terms of memory and performance. While creating an object involves additional overhead.
Possible confusion
Since JavaScript treats objects and primitives differently, using new Object()can lead to confusing situations where you’re unintentionally dealing with an object instead of a primitive, which might take you to a nest of bugs.
In most cases, it’s better to use primitives instead.
// ❌ Avoid const str = new String(); const num = new Number(); const bool = new Boolean(); const obj = new Object(); const arr = new Array(); const regEx = new RegExp(); const func = new Function(); // ✅ Use const str = "JavaScript"; const num = 10; const bool = true; const obj = {}; const arr = []; const regEx = /()/; const func = function() {};
2 — Avoid using let with arrays and objects
First of all, let’s be clear… Using let with arrays and objects is not inherently problematic at all. But there are some specific considerations that might lead you to avoid it in certain cases:
Reassignment Vs. Mutation
As we all know, let allows us to reassign the variable itself, which can lead to confusion or data loss. An object / array can be reassigned by accident with an entire new set of data (new object / new array).
Using const instead makes it safer and clear that the reference to the object / array won’t change, but you can still modify it’s content.
Immutability Intent
Using const, you signal to other developers you work with that the variable should not be reassigned, enhancing code readability and maintainability.
Scope
While let has a block scope, it can lead to unexpected behavior in loops or conditional statements. By using const, the variable remains in scope without the risk of unintentional reassignment.
Best practice
Many coding standards and best practices encourage using const for variables that don’t need reassignment, promoting cleaner and more predictable code.
// ❌ Avoid let book = { title: "Inferno", author: "Dan Brown" }; // The book object will be overrode with string book = "Hello world"; // ✅ Use const book = { title: "Inferno", author: "Dan Brown" }; // The book object cannot be overrode book = "Hello world";
3 — Be careful with Automatic Type conversion
Also known as type coercion, in JavaScript occurs when the language automatically converts a value from one type to another. This can happen in various situations, especially during operations involving different data types:
// ❌ Avoid const str = new String(); const num = new Number(); const bool = new Boolean(); const obj = new Object(); const arr = new Array(); const regEx = new RegExp(); const func = new Function(); // ✅ Use const str = "JavaScript"; const num = 10; const bool = true; const obj = {}; const arr = []; const regEx = /()/; const func = function() {};
Beware of numbers, can be converted to string or NaN by accident. Therefor, consider implementing type testing before sensitive operations or consider using TypeScript for safe typing.
4 — Avoid using double equal comparison
== and === are comparison operators used to compare values, but they behave differently.
Abstract Equality
When using ==, JavaScript converts the values to a common type before making the comparison
// ❌ Avoid let book = { title: "Inferno", author: "Dan Brown" }; // The book object will be overrode with string book = "Hello world"; // ✅ Use const book = { title: "Inferno", author: "Dan Brown" }; // The book object cannot be overrode book = "Hello world";
When to Use ?
Use === when you want to ensure both value and type are the same, which is generally a good practice to avoid unexpected results.
Use == if you specifically need to compare values without considering their types, but this can lead to bugs and usually discouraged.
In general, consider using=== for more predictable and clear comparisons.
Note: Same thing goes with !== and !=
5 — Use Object / Array destructuring
In JavaScript, using destructuring technique with Objects and Arrays gives you several benefits.
The destructuring assignment syntax is a JavaScript expression that makes it possible to unpack values from arrays, or properties from objects, into distinct variables. As MDN web docs says.
Conciseness
It allows you to extract multiple properties from an object or elements from an array in a single statement, reducing the amount of code you need to write.
let sum = "5" + 1; // "51" (string concatenation) // In the code above, typeof sum is a string let sub = "5" - 1; // 4 (string converted to number) // In the code obove, typeof sub in a number Another example can be helpful: let lang = "JavaScript"; // typeof name is string lang = 15; // changes typeof x to a number
Clarity
Destructuring can make your code more readable by clearly showing which properties or elements you are working with.
console.log(5 == '5'); // true (number is converted to string) console.log(null == undefined); // true (considered equal) console.log(0 == false); // true (0 is converted to boolean as it's falsy value) Strict Equality With ===, the comparison checks both the value and the type. If types are different, it returns false console.log(5 === '5'); // false (different types) console.log(null === undefined); // false (different types) console.log(0 === false); // false (different types)
Default Values
You can easily assign default values if the property or element doesn’t exist.
const book = { name: 'The Lost Symbol', author: 'Dan Brown' }; const { name, price } = book; // concise extraction
Nested Destructuring
You can destructure nested objects or arrays, which can simplify accessing deeply nested data.
const colors = ['red', 'green', 'blue']; const [firstColor, secondColor] = colors; // clear intention
Function Parameters
It can be useful for function parameters, allowing you to unpack values directly.
const { height = 180 } = person; // uses default value if height is undefined
Destructuring helps streamline your code, making it cleaner and easier to maintain.
6 — Default parameters
Default parameters is a good technic to adopt to keep your code clearer and easy to read.
Default function parameters allow named parameters to be initialized with default values if no value or undefined is passed. As MDN web docs says.
Single parameter
const user = { profile: { name: 'Eren Yeager', age: 20 } }; const { profile: { name } } = user; // easy access to nested properties
Multiple Parameters
You can set defaults for multiple parameters.
function display({ name, age }) { console.log(`${name} is ${age} years old.`); }
Beware of non given parameters when passing multiples. Avoid passing the possibly undefinedor possibly non-passed parameter as the first one, or before any other passed parameters.
If you suspect that a parameter value could be not given or could be passed as undefined, ensure to pass it as the last one, as well as for multiple non given parameters.
Using Expressions as Defaults
You can use expressions to calculate default values.
// ❌ Avoid const str = new String(); const num = new Number(); const bool = new Boolean(); const obj = new Object(); const arr = new Array(); const regEx = new RegExp(); const func = new Function(); // ✅ Use const str = "JavaScript"; const num = 10; const bool = true; const obj = {}; const arr = []; const regEx = /()/; const func = function() {};
Rest Parameters with Defaults
You can combine default parameters with rest parameters.
// ❌ Avoid let book = { title: "Inferno", author: "Dan Brown" }; // The book object will be overrode with string book = "Hello world"; // ✅ Use const book = { title: "Inferno", author: "Dan Brown" }; // The book object cannot be overrode book = "Hello world";
Benefits
Improved Readability: It’s clear what defaults are used if arguments are omitted.
Less Boilerplate: Reduces the need for checking and assigning default values inside the function body.
Enhanced Flexibility: Functions can handle a wider range of inputs more gracefully.
Default parameters are a powerful feature that enhances function usability and makes your code cleaner!
7 —Use default in your Switches
Ending your switch statements with a default case is a good practice in JavaScript. The default case acts as a fallback when none of the specified cases match the input:
let sum = "5" + 1; // "51" (string concatenation) // In the code above, typeof sum is a string let sub = "5" - 1; // 4 (string converted to number) // In the code obove, typeof sub in a number Another example can be helpful: let lang = "JavaScript"; // typeof name is string lang = 15; // changes typeof x to a number
Catch-All
It provides a way to handle unexpected values, ensuring that your code doesn’t silently fail.
console.log(5 == '5'); // true (number is converted to string) console.log(null == undefined); // true (considered equal) console.log(0 == false); // true (0 is converted to boolean as it's falsy value) Strict Equality With ===, the comparison checks both the value and the type. If types are different, it returns false console.log(5 === '5'); // false (different types) console.log(null === undefined); // false (different types) console.log(0 === false); // false (different types)
Improved Readability
Including a default case makes it clear to other developers (or yourself) that you considered all possibilities.
Error Handling
It can be used for logging or throwing errors when unexpected values are encountered.
const book = { name: 'The Lost Symbol', author: 'Dan Brown' }; const { name, price } = book; // concise extraction
Always include a default case if there's a possibility of receiving unexpected input.
Use the default case to provide useful feedback or logging, especially in debugging scenarios.
Consider using a default case to set a fallback value if applicable.
Adding a default case to your switch statements enhances code robustness and maintainability.
8 — Avoid using eval()
The eval() is a built-in JavaScript function that takes a string as an argument and evaluates it as JavaScript code. This means you can dynamically execute code that is generated at runtime.
const colors = ['red', 'green', 'blue']; const [firstColor, secondColor] = colors; // clear intention
Avoiding the use of eval() in JavaScript is widely recommended due to several important reasons.
Security Risks
Code Injection: eval() can execute arbitrary code, making your application vulnerable to code injection attacks. If user input is evaluated, an attacker could inject malicious code.
const { height = 180 } = person; // uses default value if height is undefined
Performance Issues
Slow Execution: Code executed with eval() runs slower than regular code because it has to be interpreted at runtime, bypassing certain optimizations made by JavaScript engines.
Debugging Challenges
Harder to Debug: Using eval() makes debugging difficult. Errors thrown inside eval() can be hard to trace back to the original source.
Alternatives
Instead of eval(), consider these safer alternatives:
JSON Parsing: If you’re dealing with JSON data, use JSON.parse() instead of eval().
const jsonString = '{"name": "Alice"}';
const obj = JSON.parse(jsonString); // Safe way to convert JSON string to an object
Function Constructors: If you need to dynamically create functions, consider using the Function constructor instead.
// ❌ Avoid const str = new String(); const num = new Number(); const bool = new Boolean(); const obj = new Object(); const arr = new Array(); const regEx = new RegExp(); const func = new Function(); // ✅ Use const str = "JavaScript"; const num = 10; const bool = true; const obj = {}; const arr = []; const regEx = /()/; const func = function() {};
In summary, avoid using eval() due to security risks, performance issues, and debugging difficulties. Opt for safer alternatives to achieve your goals without compromising the integrity and performance of your code.
9 — Use Strict Mode
In JavaScript, “Strict Mode” is a way to opt into a restricted variant of the language, which helps catch common coding mistakes and “unsafe” actions. It can make your code more predictable and easier to debug.
Enable Strict Mode
Globally: By placing "use strict"; at the top of a script file.
// ❌ Avoid let book = { title: "Inferno", author: "Dan Brown" }; // The book object will be overrode with string book = "Hello world"; // ✅ Use const book = { title: "Inferno", author: "Dan Brown" }; // The book object cannot be overrode book = "Hello world";
Benefits of Using Strict Mode
Prevents the use of undeclared variables: Assigning a value to an undeclared variable throws an error.
let sum = "5" + 1; // "51" (string concatenation) // In the code above, typeof sum is a string let sub = "5" - 1; // 4 (string converted to number) // In the code obove, typeof sub in a number Another example can be helpful: let lang = "JavaScript"; // typeof name is string lang = 15; // changes typeof x to a number
Eliminates this coercion: In Strict Mode, this is undefined in functions that are called without an explicit context.
console.log(5 == '5'); // true (number is converted to string) console.log(null == undefined); // true (considered equal) console.log(0 == false); // true (0 is converted to boolean as it's falsy value) Strict Equality With ===, the comparison checks both the value and the type. If types are different, it returns false console.log(5 === '5'); // false (different types) console.log(null === undefined); // false (different types) console.log(0 === false); // false (different types)
Prohibits certain syntax: Some syntax that is considered problematic or confusing is not allowed.
Common Pitfalls
Arrow functions: Note that arrow functions do not have their own this, so Strict Mode doesn't apply in the same way.
eval: Code executed within an eval statement runs in the local scope rather than the global scope.
Using Strict Mode is generally considered a best practice, especially for larger applications, as it helps you write cleaner and safer code.
10 — Keep Code DRY (Don’t Repeat Yourself)
The DRY (Don’t Repeat Yourself) principle is a key concept in software development aimed at reducing repetition in code. By ensuring that every piece of knowledge or logic is represented in a single place, you make your code easier to maintain, understand, and refactor.
Functions
Encapsulate repetitive logic in functions. This way, you can reuse the same code without duplication.
const book = { name: 'The Lost Symbol', author: 'Dan Brown' }; const { name, price } = book; // concise extraction
Modules
Use modules to organize your code. This helps keep related functions and variables together, making them reusable across different parts of your application.
const colors = ['red', 'green', 'blue']; const [firstColor, secondColor] = colors; // clear intention
Classes and Objects
Utilize classes or objects to group related data and behaviors. This encapsulation helps avoid repetition when working with similar data structures.
const { height = 180 } = person; // uses default value if height is undefined
Note: If you’re adopting the “Functional Programming” paradigm in your daily coding, consider using any other tip but this one “Classes and Objects”.
Templates and Components
In web development, use templates or components (in frameworks like React, Vue, etc.) to encapsulate UI logic and styles that are reused.
// ❌ Avoid const str = new String(); const num = new Number(); const bool = new Boolean(); const obj = new Object(); const arr = new Array(); const regEx = new RegExp(); const func = new Function(); // ✅ Use const str = "JavaScript"; const num = 10; const bool = true; const obj = {}; const arr = []; const regEx = /()/; const func = function() {};
Data Structures
Use arrays or objects to store related data rather than creating separate variables for each piece of data.
// ❌ Avoid let book = { title: "Inferno", author: "Dan Brown" }; // The book object will be overrode with string book = "Hello world"; // ✅ Use const book = { title: "Inferno", author: "Dan Brown" }; // The book object cannot be overrode book = "Hello world";
Applying the DRY principle leads to cleaner, more maintainable code. It helps minimize the risk of bugs since changes need to be made in only one place, and it enhances readability by reducing clutter. Remember that while it’s important to avoid repetition, there’s a balance to strike; over-abstracting can lead to complexity, so use your judgment when applying these principles.
11 — Use Meaningful Variable and Function Names
Using meaningful variable and function names is crucial for writing clear, maintainable, and understandable code.
Be Descriptive
Choose names that clearly describe the purpose or value of the variable or function.
let sum = "5" + 1; // "51" (string concatenation) // In the code above, typeof sum is a string let sub = "5" - 1; // 4 (string converted to number) // In the code obove, typeof sub in a number Another example can be helpful: let lang = "JavaScript"; // typeof name is string lang = 15; // changes typeof x to a number
Use Action Words for Functions
Start functions with a verb that describes the action being performed.
console.log(5 == '5'); // true (number is converted to string) console.log(null == undefined); // true (considered equal) console.log(0 == false); // true (0 is converted to boolean as it's falsy value) Strict Equality With ===, the comparison checks both the value and the type. If types are different, it returns false console.log(5 === '5'); // false (different types) console.log(null === undefined); // false (different types) console.log(0 === false); // false (different types)
Avoid Abbreviations
While short names might seem convenient, they can lead to confusion. Avoid abbreviations unless they are widely understood.
const book = { name: 'The Lost Symbol', author: 'Dan Brown' }; const { name, price } = book; // concise extraction
Use Consistent Naming Conventions
Stick to a consistent naming convention throughout your codebase, such as camelCase for variables and functions, and PascalCase for classes.
const colors = ['red', 'green', 'blue']; const [firstColor, secondColor] = colors; // clear intention
Indicate Data Type or Purpose in Names
If a variable holds a specific type of data or serves a particular purpose, include that in the name.
const { height = 180 } = person; // uses default value if height is undefined
Use Contextual Information
Consider the context in which the variable or function will be used to make names more meaningful.
const user = { profile: { name: 'Eren Yeager', age: 20 } }; const { profile: { name } } = user; // easy access to nested properties
Keep It Concise but Clear
While names should be descriptive, they shouldn’t be excessively long. Aim for a balance between clarity and brevity.
function display({ name, age }) { console.log(`${name} is ${age} years old.`); }
Use Domain-Specific Language
If you’re working in a specific domain (like finance, healthcare, etc.), use terms that are familiar to that domain.
let interestRate = 5.5; // Clear in a financial context.
function greet(name = 'Guest') { console.log(`Hello, ${name}!`); } greet(); // Output: Hello, Guest! greet('Chrollo'); // Output: Hello, Chrollo!
Refactor When Necessary
If you find that a name is no longer suitable as the code evolves, don’t hesitate to refactor it for better clarity.
function multiply(a, b = 1) { return a * b; } multiply(5); // Output: 5 multiply(5, 2); // Output: 10
Meaningful variable and function names significantly enhance code readability and maintainability. They help others (and yourself) understand the purpose and function of your code at a glance, making collaboration and debugging much easier. Always strive for clarity in your naming conventions.
12 — Avoid Global Variables
Avoiding global variables is a key practice in JavaScript (and programming in general) to maintain clean, modular, and maintainable code. Global variables can lead to unexpected behavior, naming conflicts, and difficulty in debugging.
Use Function Scope
Declare variables within functions to limit their scope and prevent them from being accessible globally.
// ❌ Avoid const str = new String(); const num = new Number(); const bool = new Boolean(); const obj = new Object(); const arr = new Array(); const regEx = new RegExp(); const func = new Function(); // ✅ Use const str = "JavaScript"; const num = 10; const bool = true; const obj = {}; const arr = []; const regEx = /()/; const func = function() {};
Use Block Scope with let and const
Utilize let and const to declare variables within blocks (like loops or conditionals), ensuring they are not accessible outside that block.
// ❌ Avoid let book = { title: "Inferno", author: "Dan Brown" }; // The book object will be overrode with string book = "Hello world"; // ✅ Use const book = { title: "Inferno", author: "Dan Brown" }; // The book object cannot be overrode book = "Hello world";
Modularize Your Code
Organize your code into modules. Use ES6 modules or IIFE (Immediately Invoked Function Expressions) to encapsulate variables.
let sum = "5" + 1; // "51" (string concatenation) // In the code above, typeof sum is a string let sub = "5" - 1; // 4 (string converted to number) // In the code obove, typeof sub in a number Another example can be helpful: let lang = "JavaScript"; // typeof name is string lang = 15; // changes typeof x to a number
Encapsulate in Objects:
Group related variables and functions within an object to avoid polluting the global scope.
console.log(5 == '5'); // true (number is converted to string) console.log(null == undefined); // true (considered equal) console.log(0 == false); // true (0 is converted to boolean as it's falsy value) Strict Equality With ===, the comparison checks both the value and the type. If types are different, it returns false console.log(5 === '5'); // false (different types) console.log(null === undefined); // false (different types) console.log(0 === false); // false (different types)
Use Local Storage Wisely
If you need to persist data, consider using local storage, session storage, or indexedDB instead of global variables.
const book = { name: 'The Lost Symbol', author: 'Dan Brown' }; const { name, price } = book; // concise extraction
Limit the Use of Globals
If you must use global variables, limit their use to configuration constants or application-wide settings. Name them clearly to indicate their global nature.
const colors = ['red', 'green', 'blue']; const [firstColor, secondColor] = colors; // clear intention
Avoid Side Effects
When designing functions, avoid modifying global variables. This keeps functions predictable and easier to test.
const { height = 180 } = person; // uses default value if height is undefined
Use ‘this' Wisely
In object-oriented programming, use this to manage state within instances instead of relying on global variables.
const user = { profile: { name: 'Eren Yeager', age: 20 } }; const { profile: { name } } = user; // easy access to nested properties
By avoiding global variables, you enhance the modularity and maintainability of your code. It helps prevent naming conflicts and unintended side effects, making your code more predictable and easier to work with. Following these best practices will lead to cleaner and more manageable codebases.
13 — Use Promises and Async/Await for Asynchronous Code
Using Promises and async/await in JavaScript helps manage asynchronous operations more effectively, making your code cleaner and easier to read.
Understanding Promises
A Promise is an object representing the eventual completion (or failure) of an asynchronous operation and its resulting value.
You can create a Promise using the Promise constructor:
function display({ name, age }) { console.log(`${name} is ${age} years old.`); }
Consuming a Promise
You can handle the outcome of a Promise using .then() for success and .catch() for error handling.
function greet(name = 'Guest') { console.log(`Hello, ${name}!`); } greet(); // Output: Hello, Guest! greet('Chrollo'); // Output: Hello, Chrollo!
Chaining Promises
You can chain multiple asynchronous operations using Promises.
function multiply(a, b = 1) { return a * b; } multiply(5); // Output: 5 multiply(5, 2); // Output: 10
Using Async/Await
async/await provides a more synchronous way to write asynchronous code, making it easier to read and maintain.
Declaring an Async Function:
Use the async keyword before a function to define it as an asynchronous function.
// ❌ Avoid const str = new String(); const num = new Number(); const bool = new Boolean(); const obj = new Object(); const arr = new Array(); const regEx = new RegExp(); const func = new Function(); // ✅ Use const str = "JavaScript"; const num = 10; const bool = true; const obj = {}; const arr = []; const regEx = /()/; const func = function() {};
Calling Async Functions
You can call an async function just like a regular function. But, beware it will always return a Promise.
// ❌ Avoid let book = { title: "Inferno", author: "Dan Brown" }; // The book object will be overrode with string book = "Hello world"; // ✅ Use const book = { title: "Inferno", author: "Dan Brown" }; // The book object cannot be overrode book = "Hello world";
Handling Multiple Asynchronous Operations
You can use Promise.all to run multiple promises in parallel and wait for all of them to resolve.
let sum = "5" + 1; // "51" (string concatenation) // In the code above, typeof sum is a string let sub = "5" - 1; // 4 (string converted to number) // In the code obove, typeof sub in a number Another example can be helpful: let lang = "JavaScript"; // typeof name is string lang = 15; // changes typeof x to a number
Error Handling
Both Promises and async/await provide ways to handle errors gracefully.
Using .catch() with Promises:
console.log(5 == '5'); // true (number is converted to string) console.log(null == undefined); // true (considered equal) console.log(0 == false); // true (0 is converted to boolean as it's falsy value) Strict Equality With ===, the comparison checks both the value and the type. If types are different, it returns false console.log(5 === '5'); // false (different types) console.log(null === undefined); // false (different types) console.log(0 === false); // false (different types)
Using try/catch with Async/Await:
const book = { name: 'The Lost Symbol', author: 'Dan Brown' }; const { name, price } = book; // concise extraction
Using Promises and async/await makes handling asynchronous operations in JavaScript much more manageable. They help avoid callback hell and improve code readability. Embracing these patterns will lead to cleaner, more maintainable, and error-resistant code.
14 — Document Your Code
Documenting your code is essential for maintaining clarity, aiding collaboration, and ensuring long-term maintainability.
Use Clear Comments
Explain “Why,” Not “What”: Focus on explaining why you did something rather than what the code does. The code itself should be readable enough to convey what it does.
const colors = ['red', 'green', 'blue']; const [firstColor, secondColor] = colors; // clear intention
Comment Complex Logic: For complex or non-obvious sections of code, provide detailed explanations.
const { height = 180 } = person; // uses default value if height is undefined
Use Docstring Style Comments
In JavaScript, especially when using JSDoc, you can document functions, classes, and methods using structured comments.
const user = { profile: { name: 'Eren Yeager', age: 20 } }; const { profile: { name } } = user; // easy access to nested properties
Maintain a README File
For projects, maintain a README.md file that provides an overview, installation instructions, usage examples, and contribution guidelines.
Effective documentation makes your code more understandable and maintainable, helping both current and future developers (including yourself) work efficiently. By incorporating these practices into your development workflow, you’ll foster better collaboration and reduce the learning curve for anyone interacting with your code.
The above is the detailed content of JavaScript Best Practices. For more information, please follow other related articles on the PHP Chinese website!