DEV Community

Cover image for JavaScript Tips and Tricks for Clean and Efficient Code
JavaScript Jobs
JavaScript Jobs

Posted on

JavaScript Tips and Tricks for Clean and Efficient Code

Mastering JavaScript: Advanced Tips and Tricks for Clean and Efficient Code

JavaScript, being one of the most widespread programming languages, is constantly evolving, offering developers new opportunities to write cleaner, more concise, and more efficient code. Modern JavaScript features significantly reduce code volume and improve readability, abstracting away complexities once associated with server-side development. Mastering these techniques will help you write "good JavaScript" using modern capabilities.

True mastery in JavaScript requires not only learning syntax but also decades of building complex applications. This article presents a selection of key tips and tricks to help you improve your JavaScript skills, making your code more performant and easier to maintain.

Asynchronous Programming with async/await: A Revolution in Asynchronicity

async/await is one of the most significant improvements in JavaScript, allowing asynchronous code to be expressed in a synchronous, more readable format. It significantly simplifies working with promises, eliminating the need for .then() chains, which can lead to "callback hell" and complicate code readability.

To use async/await:

  • Define a function as async, which will make it return a promise.
  • Use the await keyword before calling a promise to wait for its resolution and get the actual variable value.

Example: Instead of using nested .then():

random().then(first => {
    return random().then(second => {
        return random().then(third => {
            // ...
        });
    });
});
Enter fullscreen mode Exit fullscreen mode

A more readable approach with async/await is used:

async function getRandomNumbers() {
    const first = await random(); // waits for the first random number
    const second = await random(); // waits for the second random number
    const third = await random(); // waits for the third random number
    // Now you can work with first, second, third as with ordinary variables
    return first + second + third;
}
Enter fullscreen mode Exit fullscreen mode

This approach makes the code much easier to read and understand, as you can simply follow it line by line.

Destructuring: Flexibility in Data Handling

Destructuring is a powerful JavaScript feature that allows you to "unpack" values from arrays or properties from objects into separate variables, making the code more concise and readable.

Swapping Variable Values

You can quickly swap the values of two variables without using a temporary variable. This method creates a new array on the right side of the expression and then destructures its values into variables on the left side. While this creates temporary arrays, it is not a significant performance concern for most JavaScript applications.

Object Destructuring with Default Values

When extracting properties from an object, you can specify default values that will be used if the property is missing from the object. For example, { a = 5, b = 10 }. If property a exists in the object, its value will be used; otherwise, the default value (in this case, 5) will be used.

Destructuring in Function Parameters

This technique allows you to directly extract the necessary properties from an object passed into a function, right in the parameter list. This significantly reduces code repetition and improves readability. If you prefer not to use bracket syntax in the argument or are working with multiple objects, properties can be destructured within the function itself. For example, instead of function(e) { e.target.className }, you can write function({ target }) { target.className }.

Spread Syntax: A Powerful Tool for Arrays and Objects

The spread operator (...) allows for convenient copying, merging, and modifying of arrays and objects, promoting the creation of immutable data structures.

Merging Objects and Creating Immutable Copies

You can easily create new objects by merging properties of existing objects. Properties located further to the right take precedence. This approach allows you to create new objects instead of mutating the originals, which is important for code predictability. For example, const newObj = { ...obj1, ...obj2 }.

Copying and Manipulating Arrays

The spread operator creates a shallow copy of an array. It can also be used to add elements to the beginning, end, or middle of an array, creating a new array without mutating the original. For example, [...oldArr, newItem] or [newItem, ...oldArr].

Modern Array Methods: A Functional Approach to Data

Instead of traditional for loops, which can be cumbersome and lead to value mutation, modern array methods (map, filter, reduce) offer a cleaner, more readable, and functional approach.

reduce() for Value Aggregation

The reduce() method applies a reducer function to each element in an array (from left to right), reducing it to a single value. For example, to sum all elements of an array: array.reduce((accumulator, currentValue) => accumulator + currentValue).

map() for Element Transformation

The map() method creates a new array by applying a specified function to each element of the original array. For example, to add a 10% tax to each order value: array.map(amount => amount * 1.1).

filter() for Element Selection

The filter() method creates a new array containing only those elements from the original array for which the callback function returns true. For example, to keep only numbers greater than 100: array.filter(value => value > 100). It is also possible to filter all "falsy" values (such as 0, false, "" (empty string), null, undefined) from an array by passing the Boolean function as an argument: array.filter(Boolean).

Chaining Array Methods

These methods can be easily chained together to perform complex operations in a single line of code, making the code incredibly expressive.

find() for Advanced Searching

The find() method allows you to find the first element in an array that satisfies a condition defined in the callback function.

Improved String Handling: Template Literals

Template literals, using backticks (`), significantly simplify string manipulation by allowing variables and expressions to be interpolated directly into the string, and by creating multiline strings without special characters. This eliminates the need for string concatenation with + and manual whitespace management, making the code more readable and maintainable.

Copying Arrays and Objects

When working with data, it is often necessary to create a copy of an array or object.

Shallow Copying with the Spread Operator (...)

For arrays: a shallow copy of an array can be created using the syntax const newArray = [...originalArray]. This is convenient for adding elements to the beginning, end, or middle of an array without mutating the original: [...oldArr, newItem] or [newItem, ...oldArr].

For objects: the spread operator also allows you to create new objects by merging properties of existing objects, with properties of objects located further to the right taking precedence. For example, const newObj = { ...obj1, ...obj2 }. This is more concise than Object.assign(). It is important to remember that this is a shallow copy. If the original array or object contains nested objects or arrays, their copies will refer to the same nested structures as the original.

Deep Copying with JSON.stringify() and JSON.parse()

For small objects and arrays that do not contain functions, undefined, or other non-serializable data types, a deep copy can be performed using JSON.parse(JSON.stringify(originalObject)). This will create an entirely new structure, including nested arrays and objects, so they will not refer to the originals.

Working with Objects: Dynamic Keys and Destructuring

Computed Property Names

If you need to use a variable as a property name when creating an object, enclose the variable name in square brackets: {[propertyName]: value}. This is especially useful when object keys are generated dynamically.


Managing Default Values and Safe Property Access

Simple Default Values with the OR Operator (||)

To set a default value if a variable is "falsy" — i.e., null, undefined, 0, "" (empty string) — you can use the || (OR) operator. For example, const username = getUsername() || 'dom';. If getUsername() returns a "truthy" value, it is used; otherwise, 'dom' is used.

Nullish Coalescing Operator (??)

This operator provides a more precise way to handle default values than ||. It returns the right-hand operand only if the left-hand operand is null or undefined, not any "falsy" value. This allows, for example, to retain 0 or empty strings as valid values if needed.

Optional Chaining (?.)

This operator allows safe access to object properties that may be null or undefined without throwing an error. For example, object?.name. If object is undefined, the code will not throw an error but will simply return undefined.


Additional Useful Techniques

Removing Duplicates from an Array with Set

Set is a data structure that stores only unique values. To remove duplicates from an array, you can create a new Set from the array and then convert it back to an array: Array.from(new Set(array)).

Human-Readable Number Formatting with toLocaleString()

To make numbers more readable (e.g., add thousand separators), use the number.toLocaleString() method. You can also pass a locale string (e.g., 'es' for Spanish) to format the number according to the rules of a specific region, as different parts of the world display numbers differently (e.g., swapping commas and periods).

Debugging with console.log() and console.trace()

For more informative logging of multiple variables, instead of outputting them separately, you can group them into an object: console.log({var1, var2}). This will show the variable name and its value in the console. console.trace() shows the call stack, which is very useful for tracking where a function was called from.

Mastering these advanced tips and techniques in JavaScript will enable you to write cleaner, more efficient, and more maintainable code, enhancing your productivity as a developer. You can find more insights on optimizing your development process in our detailed guide on How to 10x Developer Productivity: The Uncomfortable Truth About AI and explore practical strategies to boost your earnings in Earn More as a Developer with Smart Career Moves.


Top comments (0)