Home >Web Front-end >JS Tutorial >Best Practices in Modern JavaScript - Part 1

Best Practices in Modern JavaScript - Part 1

Patricia Arquette
Patricia ArquetteOriginal
2024-12-07 02:26:11281browse

Mejores Prácticas en JavaScript Moderno - Parte 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.

1. Project rules are the most important thing

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.

2. Use updated JavaScript

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.

3. Use let and const instead of var

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.

When to use let and when to use const?

  • Use const whenever a variable does not change its reference or value. This makes your code easier to understand and reduces errors by protecting immutable values.
  • Use let if you need to reassign the variable's value in the future, but only in cases where it is necessary.

Golden rule: Use const by default. If you later need to change the value, switch to let.

Practical examples

  1. const for constant values:
const PI = 3.14159;
console.log(PI); // 3.14159

PI = 3; // TypeError: Assignment to constant variable.
  1. let for changing values:
let count = 0;

for (let i = 1; i <= 3; i++) {
  count += i; 
}
console.log(count); // 6
  1. Scope comparison (let vs var):
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
  1. Avoid problems with loops and callbacks:

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.

4. Opt for Classes: Simplicity in JavaScript

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?

  • More readable and less prone to errors.
  • They facilitate inheritance with extends and the use of super.
  • More compatible with modern tools and ES6 standards.

5. Use Real Private Fields in JavaScript

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.

Why use real private fields?

  1. Authentic Encapsulation: Protect your data and ensure that it cannot be accessed or modified outside the context of the class.
  2. Readability: Using the # prefix makes it clear which properties are private, improving code understanding.
  3. Data security: Prevent accidental errors or unintentional access to internal properties.

Basic example:

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100); // Imprime 3, 3, 3
}

Advanced Example: Protected Counters

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.

Considerations

  • Private fields cannot be accessed even by subclasses.
  • If you need to interact with private data in inheritances, consider using protected methods instead of private fields.

6. Use arrow functions

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.

Why use arrow functions?

  1. Shorter, cleaner syntax: Ideal for keeping code more readable.
  2. Context of this automatic: Perfect to avoid errors in callbacks or methods.
  3. Ideal use in inline functions: Like the ones we use in map, filter or events.

Practical examples

1. With map to transform arrays

let count = 0;

for (let i = 1; i <= 3; i++) {
  count += i; 
}
console.log(count); // 6

2. With filter to filter elements

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

3. With reduce to add values

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100); // Imprime 3, 3, 3
}

4. In DOM events (be careful with the context!)

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
}

Tip: when to not use them

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.

7. Null coalescence operator (??)

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.

What is the key difference?

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.

Basic example:

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

Practical cases with ??

  • Set default values ​​without overwriting valid values:
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
  • Validate optional configuration:

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
}
  • Avoid errors when working with optional properties:
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!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn