Home >Web Front-end >JS Tutorial >Best Practices in Modern JavaScript - Part 1
JavaScript is, without a doubt, the most used programming language in the world and has an enormous influence on one of the most important technologies in our daily lives: the Internet. With this power comes great responsibility, and the JavaScript ecosystem has been evolving rapidly, making it difficult to keep up with best practices.
In this article, we'll explore some of the top best practices in modern JavaScript to write cleaner, maintainable, and efficient code.
Each project may have specific rules to maintain code consistency. These rules will always take precedence over any external recommendations, including those in this article. Before implementing a practice in a project, make sure it is aligned with the established rules and that all team members agree.
JavaScript has evolved tremendously since its creation in 1995. Many old practices you find on the internet may be outdated. Before implementing a technique, verify that it is compatible with the current version of the language.
Although var is still valid, its use is considered obsolete and, in many cases, can introduce bugs that are difficult to trace due to its functional scope. On the other hand, let and const give us a more predictable and safe scope, being limited to the block where they are declared.
Golden rule: Use const by default. If you later need to change the value, switch to let.
const PI = 3.14159; console.log(PI); // 3.14159 PI = 3; // TypeError: Assignment to constant variable.
let count = 0; for (let i = 1; i <= 3; i++) { count += i; } console.log(count); // 6
if (true) { let blockScoped = "Solo dentro del bloque"; var functionScoped = "Disponible fuera también"; } console.log(functionScoped); // "Disponible fuera también" console.log(blockScoped); // ReferenceError: blockScoped is not defined
With var you can have unexpected behaviors in loops, especially in asynchronous functions:
for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 100); // Imprime 3, 3, 3 }
While let fixes this:
const PI = 3.14159; console.log(PI); // 3.14159 PI = 3; // TypeError: Assignment to constant variable.
Replacing var with let and const is not only good practice, but it also helps make your code safer, more readable, and easier to debug. Make the future you thank you.
Using Function.prototype for object-oriented programming in JavaScript is an older and often error-prone approach. On the contrary, the classes introduced in ES6 offer a more intuitive syntax that is closer to other object-oriented languages, facilitating the readability and maintenance of the code.
Example with classes (modern and clear):
let count = 0; for (let i = 1; i <= 3; i++) { count += i; } console.log(count); // 6
Comparison with Function.prototype (complicated and less intuitive):
if (true) { let blockScoped = "Solo dentro del bloque"; var functionScoped = "Disponible fuera también"; } console.log(functionScoped); // "Disponible fuera también" console.log(blockScoped); // ReferenceError: blockScoped is not defined
As you can see, the prototype-based approach requires more steps to define methods and can be more confusing for less experienced developers. Not only are classes easier to read, but they also promote cleaner, more modular code.
Why use classes?
For a long time, JavaScript developers used conventions like an underscore (_) to simulate private fields in classes. However, this was just a visual convention, as the properties were still accessible from outside the class. Now, thanks to real private fields, we can guarantee that certain properties are completely inaccessible from the outside.
⚠️ Attention: This feature may not be available in the console of some browsers.
for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 100); // Imprime 3, 3, 3 }
Imagine that you want to create a class that records the number of visits to a page, but you don't want anyone to be able to manipulate that counter directly.
const PI = 3.14159; console.log(PI); // 3.14159 PI = 3; // TypeError: Assignment to constant variable.
In this case, the #visits counter is completely protected from external access, which guarantees that its value is not improperly manipulated.
arrow functions are a modern and elegant way to write functions in JavaScript. They offer a shorter syntax and, unlike traditional functions, automatically inherit the context of this, which avoids common problems in object-oriented programming.
They are especially useful in higher order functions like map, filter and reduce, where we need to pass functions as arguments.
let count = 0; for (let i = 1; i <= 3; i++) { count += i; } console.log(count); // 6
if (true) { let blockScoped = "Solo dentro del bloque"; var functionScoped = "Disponible fuera también"; } console.log(functionScoped); // "Disponible fuera también" console.log(blockScoped); // ReferenceError: blockScoped is not defined
for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 100); // Imprime 3, 3, 3 }
When we use arrow functions in events, the this context will not change, which can be useful:
for (let i = 0; i < 3; i++) { setTimeout(() => console.log(i), 100); // Imprime 0, 1, 2 }
Although arrow functions are great, they are not ideal for everything. Avoid them in cases where you need to access the function context itself, such as in functions that use dynamic this or if you need to write methods in prototypes.
Example where a normal function is better:
class Persona { constructor(nombre) { this.nombre = nombre; } obtenerNombre() { return this.nombre; } } const persona = new Persona('Juan'); console.log(persona.obtenerNombre()); // 'Juan'
If you changed print to an arrow function, you would lose the context of this.
Let's improve that section! I added some context, a clearer explanation, and some additional examples to make it more complete and useful.
The null coalescence operator (??) is a more precise alternative to the logical operator || to assign default values. While || considers "falsy" to be values such as 0, false or "", the operator ?? only evaluates null or undefined as "missing" values. This makes it a safer and more specific option in many cases.
With ||, any "falsy" value will be replaced by the default value.
With ??, only replaces values that are null or undefined. This allows you to keep "falsy" values like 0 or "" if they are valid in your context.
const PI = 3.14159; console.log(PI); // 3.14159 PI = 3; // TypeError: Assignment to constant variable.
On the other hand, with ||:
let count = 0; for (let i = 1; i <= 3; i++) { count += i; } console.log(count); // 6
if (true) { let blockScoped = "Solo dentro del bloque"; var functionScoped = "Disponible fuera también"; } console.log(functionScoped); // "Disponible fuera también" console.log(blockScoped); // ReferenceError: blockScoped is not defined
Suppose you have a system that allows you to customize options:
for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 100); // Imprime 3, 3, 3 }
for (let i = 0; i < 3; i++) { setTimeout(() => console.log(i), 100); // Imprime 0, 1, 2 }
The above is the detailed content of Best Practices in Modern JavaScript - Part 1. For more information, please follow other related articles on the PHP Chinese website!