首页 > web前端 > js教程 > 正文

每个 Node 开发人员都应该掌握的基本 JavaScript 概念

Patricia Arquette
发布: 2024-10-11 10:26:30
原创
463 人浏览过

Essential JavaScript Concepts Every Node Developer Should Conquer

Maîtriser les concepts JavaScript de base pour les développeurs Node.js

JavaScript a ouvert la voie en matière de codage en étant le langage de choix pour le développement frontend et backend, avec NodeJs à l'avant-garde. Avant que le buzz autour du JavaScript côté serveur ne devienne cool, tout le monde reconnaissait JS comme le courageux non-conformiste du mouvement. Alors que des plates-formes plus récentes telles que Deno et Bun ont commencé à offrir de la concurrence, NodeJs reste l'épine dorsale des applications Web et des logiciels système, avec des millions de lignes de code écrites. et exécuté en utilisant JS. Construit sur son architecture asynchrone unique et des outils comme Express, NodeJs est à la fois une aubaine et un fléau pour les développeurs. Pour écrire des applications efficaces, évolutives et maintenables, il est essentiel de comprendre les concepts JavaScript clés.

Ces concepts de base vous permettent d'aller au-delà des défis courants tels que le threading, la portée de fermeture et le code asynchrone, en libérant JavaScript dans NodeJs pour une puissance maximale. Ce guide couvre 18 des techniques JavaScript les plus importantes pour vous aider à écrire du code complexe et performant tout en évitant les pièges courants et en naviguant efficacement dans la boucle des événements. Que vous travailliez sur des API, des opérations d'E/S ou des optimisations de mémoire, la maîtrise de ces concepts élèvera votre développement NodeJs au niveau supérieur.

1. Fermetures JavaScript

  • Une fermeture est une fonctionnalité en JavaScript où une fonction interne a accès aux variables de sa fonction externe, même une fois l'exécution de la fonction externe terminée. Les fermetures préservent cet accès, gardant les variables de la fonction externe actives pour une utilisation dans la fonction interne. Cela vous permet de créer des états privés et d'encapsuler des données dans votre code, un concept particulièrement utile lorsqu'il s'agit de rappels, de gestionnaires d'événements ou de modules dans Node.js.

Exemple :

function outerFunction() {
    const outerVariable = "I am from outer function!";
    return function innerFunction() {
        console.log(outerVariable);
    };
}

const innerFunc = outerFunction();
innerFunc(); // Output: "I am from outer function!"
登录后复制

Cet exemple montre une fermeture où la fonction interne conserve l'accès à la variable de la fonction externe même après la fin de son exécution.

2. Prototypes JavaScript

  • Les prototypes sont un élément clé du système d'héritage de JavaScript. Chaque fonction JavaScript (qui inclut des objets, car les fonctions sont également des objets) possède une propriété prototype, qui vous permet de définir des méthodes et des propriétés partagées. Les objets peuvent hériter des comportements d'un autre objet via la chaîne de prototypes. Même si le JavaScript moderne offre une syntaxe de classe, sous le capot, les prototypes gèrent toujours l'héritage. Comprendre cela aide à créer un code orienté objet plus efficace et économisant de la mémoire dans Node.js.

Exemple :

function Person(name) {
    this.name = name;
}
Person.prototype.greet = function() {
    console.log(`Hello, my name is ${this.name}`);
};

const john = new Person("John");
john.greet(); // Output: "Hello, my name is John"
登录后复制

Ici, greet est défini sur le prototype Person, permettant à toutes les instances de Person de partager cette méthode, ce qui économise de la mémoire.

3. Propriétés privées avec hashtags

  • L'introduction des champs privés dans JavaScript utilise le symbole # pour déclarer des propriétés véritablement privées au sein des classes. Ces champs privés ne sont pas accessibles depuis l’extérieur de la classe, pas même via des prototypes. Il s'agit d'une manière beaucoup plus simple de gérer l'encapsulation que la convention consistant à utiliser des traits de soulignement pour signaler une propriété privée, qui n'a jamais été réellement privée dans le langage.

Exemple :

class User {
    #name; // Private property
    constructor(name) {
        this.#name = name;
    }
    getName() {
        return this.#name;
    }
}

const user = new User("Alice");
console.log(user.getName()); // Output: "Alice"
// console.log(user.#name); // Error: Private field '#name' must be declared in an enclosing class
登录后复制

Cet exemple montre comment le symbole # est utilisé pour déclarer une propriété véritablement privée qui n'est pas accessible de l'extérieur de la classe.

4. Propriétés privées avec fermetures

  • Avant l'introduction des champs privés, les fermetures étaient souvent utilisées pour imiter les propriétés privées. En définissant des variables dans la portée d'une fonction et en renvoyant des méthodes qui accèdent à ces variables, vous pouvez créer efficacement des propriétés privées. Cette méthode fonctionne toujours et est particulièrement utile lorsque vous devez maintenir la confidentialité et l'encapsulation sans vous fier à la syntaxe de classe.

Exemple :

function createCounter() {
    let count = 0; // Private variable
    return {
        increment: function() {
            count++;
        },
        getCount: function() {
            return count;
        }
    };
}

const counter = createCounter();
counter.increment();
console.log(counter.getCount()); // Output: 1
登录后复制

Dans cet exemple, count est encapsulé dans la fermeture, fournissant un état privé pour le compteur.

5. Modules JavaScript

  • JavaScript dispose de plusieurs systèmes de modules. Node.js utilisait traditionnellement le système de modules CommonJS, qui repose sur require et module.exports. Cependant, ES6 a introduit un système de modules natifs, qui utilise l'importation et l'exportation, désormais pris en charge dans Node.js et dans les navigateurs. Bien que les modules ES6 représentent l'avenir, de nombreux systèmes et bibliothèques existants utilisent toujours CommonJS, il est donc important de comprendre les deux pour les développeurs Node.js.

Exemple :

// module.js
export const greeting = "Hello, World!";
export function greet() {
    console.log(greeting);
}

// main.js
import { greet } from './module.js';
greet(); // Output: "Hello, World!"
登录后复制

This example illustrates how to use ES6 modules to export and import variables and functions between files.

6. Error Handling

  • Error handling in JavaScript, especially in Node.js, is critical for building robust applications. Node.js is asynchronous, which introduces unique challenges. While try/catch is useful for synchronous code, handling errors in asynchronous code requires approaches like .catch() with promises or async/await. Proper error handling ensures your app remains stable and doesn't fail silently, making it easier to debug and maintain.

Example:

async function fetchData() {
    try {
        const response = await fetch('https://api.example.com/data');
        if (!response.ok) throw new Error('Network response was not ok');
        const data = await response.json();
        console.log(data);
    } catch (error) {
        console.error('Fetch error:', error);
    }
}

fetchData(); // Handles fetch errors gracefully.
登录后复制

Here, error handling is implemented using try/catch with asynchronous code to manage potential errors when fetching data.

7. Currying

  • Currying transforms a function that takes multiple arguments into a series of functions, each taking one argument at a time. This allows for partial application, where you can provide some arguments upfront and return a function that accepts the rest. Currying is a key technique in functional programming, which is gaining popularity in JavaScript for creating highly reusable and composable functions.

Example:

function multiply(a) {
    return function(b) {
        return a * b;
    };
}

const double = multiply(2);
console.log(double(5)); // Output: 10
登录后复制

In this example, the multiply function is curried, allowing for partial application by creating a double function.

8. Apply, Call, and Bind Methods

  • These methods give you explicit control over the context (this) within which a function executes. call() and apply() invoke a function immediately, with call() passing arguments individually and apply() passing them as an array. bind(), on the other hand, returns a new function with a bound context that can be invoked later. Mastering these methods helps in ensuring functions execute in the correct context, especially in event-driven environments like Node.js.

Example:

const obj = { value: 42 };

function showValue() {
    console.log(this.value);
}

showValue.call(obj); // Output: 42
showValue.apply(obj); // Output: 42

const boundShowValue = showValue.bind(obj);
boundShowValue(); // Output: 42
登录后复制

This example demonstrates how call, apply, and bind control the context of this when invoking functions.

9. Memoization

  • Memoization is an optimization technique where the results of expensive function calls are cached, so that future calls with the same input return the cached result rather than recalculating. This can significantly improve performance, especially in scenarios like recursion, where the same function might be called multiple times with the same arguments.

Example:

function memoize(fn) {
    const cache = {};
    return function(...args) {
        const key = JSON.stringify(args);
        if (cache[key]) return cache[key];
        const result = fn(...args);
        cache[key] = result;
        return result;
    };
}

const fibonacci = memoize(n => (n <= 1 ? n : fibonacci(n - 1) + fibonacci(n - 2)));
console.log(fibonacci(10)); // Output: 55 (calculated efficiently)
登录后复制

This example shows how memoization can optimize the Fibonacci function by caching results of previous calls.

10. Immediately Invoked Function Expressions (IIFE)

  • An IIFE is a function that is executed immediately after it's defined. It helps in isolating variables and avoiding global scope pollution, which is useful for creating self-contained modules. While less common in modern JavaScript (due to the advent of modules), IIFEs are still valuable for certain use cases where encapsulation is required.

Example:

(function() {
    const privateVariable = "I'm private!";
    console.log(privateVariable);
})(); // Output: "I'm private!"
登录后复制

An IIFE is used here to create a scope that keeps privateVariable from polluting the global namespace.

11. Working with Function Arguments

  • JavaScript provides flexibility in handling function arguments. You can set default values, use the rest operator to collect additional arguments, or access arguments using the arguments object (though this is less common in modern code). This flexibility is key to creating adaptable and robust functions in Node.js, particularly when dealing with asynchronous patterns or varying input.

Example:

function sum(...numbers) {
    return numbers.reduce((total, num) => total + num, 0);
}

console.log(sum(1, 2, 3, 4)); // Output: 10
登录后复制

This example uses the rest operator to collect multiple arguments into an array, allowing flexible function signatures.

12. Asynchronous Programming and the Event Loop

  • Node.js operates on a single-threaded, non-blocking model powered by the event loop, which allows it to handle thousands of operations concurrently without blocking the main thread. Mastering the event loop and how asynchronous operations are managed through callbacks, promises, and async/await is crucial for building performant Node.js applications. Mismanagement of the event loop can lead to bottlenecks and degraded performance.

Example:

console.log("Start");

setTimeout(() => {
    console.log("Timeout executed");
}, 1000);

console.log("End"); 
// Output: "Start", "End", "Timeout executed" (after 1 second)
登录后复制

This example illustrates how the event loop manages asynchronous code, allowing other operations to run while waiting for the timeout.

13. Promises and async/await

  • Promises provide a more structured way to handle asynchronous operations than traditional callbacks, helping to avoid “callback hell.” The async/await syntax builds on promises, allowing developers to write asynchronous code that looks and behaves like synchronous code, improving readability and maintainability.

Example:

function fetchData() {
    return new Promise((resolve) => {
        setTimeout(() => resolve("Data received"), 1000);
    });
}

async function getData() {
    const data = await fetchData();
    console.log(data); // Output: "Data received"
}

getData();
登录后复制

This example demonstrates the use of async/await to work with promises in a more readable way.

14. Event Emitters

  • Event-driven architecture is central to Node.js. The EventEmitter class allows you to create and manage events, enabling components to communicate with each other efficiently. Learning how to use event emitters to trigger and listen for custom events can lead to cleaner, more decoupled code.

Example:

const EventEmitter = require('events');
const myEmitter = new EventEmitter();

myEmitter.on('event', () => {
    console.log('An event occurred!');
});

myEmitter.emit('event'); // Output: "An event occurred!"
登录后复制

Here, an event emitter is created, and an event is triggered, demonstrating the basic event-driven architecture of Node.js.

15. Streams and Buffers

  • Node.js handles I/O operations efficiently using streams and buffers. Streams allow you to process data chunk by chunk, which is particularly useful for large datasets like file uploads, where loading everything into memory at once would be inefficient. Buffers are used to store binary data, which is critical when working with streams. Understanding streams and buffers helps you optimize performance and handle large data more efficiently.

Example:

const fs = require('fs');
const readableStream = fs.createReadStream('file.txt');

readableStream.on('data', (chunk) => {
    console.log(`Received ${chunk.length} bytes of data.`);
});

readableStream.on('end', () => {
    console.log('No more data.');
});
登录后复制

This example shows how to read data from a file in chunks using streams, which is efficient for large files.

16. Higher-Order Functions

  • Higher-order functions are functions that either take other functions as arguments or return them. JavaScript functions are first-class citizens, meaning they can be passed around like any other variable. This concept is used extensively in Node.js, especially when working with callbacks, promises, and array methods like map(), filter(), and reduce().

Example:

function applyOperation(a, b, operation) {
    return operation(a, b);
}

const add = (x, y) => x + y;
console.log(applyOperation(5, 10, add)); // Output: 15
登录后复制

In this example, applyOperation is a higher-order function that takes another function as an argument to perform operations on the inputs.

17. Garbage Collection and Memory Management

  • JavaScript’s memory management is handled by an automatic garbage collector, which frees up memory occupied by objects that are no longer in use. However, understanding how the garbage collector works is essential in Node.js, particularly for preventing memory leaks in long-running applications. You can manage memory usage efficiently by avoiding closures that inadvertently capture unnecessary variables or handling large datasets appropriately with streams.

Example:

function createLargeArray() {
    const largeArray = new Array(1000000).fill('Data');
    // Do something with the array
}

createLargeArray(); // The largeArray will be eligible for garbage collection after this function execution
登录后复制

This example illustrates how objects can be garbage collected when they are no longer accessible, thus freeing up memory.

18. Timers

  • Node.js provides a number of global functions for scheduling code execution: setTimeout(), setInterval(), and setImmediate(). These timers are often used in asynchronous programming, especially when you need to delay or repeat tasks.

Example:

console.log('Start');

setTimeout(() => {
    console.log('Executed after 2 seconds');
}, 2000);

setInterval(() => {
    console.log('Executed every second');
}, 1000);
登录后复制

In this example, setTimeout schedules a one-time execution after 2 seconds, while setInterval repeatedly executes the function every second.

19. Template Literals

  • Template literals provide a way to work with strings more easily. They allow for multi-line strings and string interpolation, making it easier to construct strings dynamically.

Example:

const name = "Alice";
const greeting = `Hello, ${name}! Welcome to JavaScript.`;
console.log(greeting); // Output: Hello, Alice! Welcome to JavaScript.
登录后复制

In this example, template literals are used to create a greeting string that incorporates a variable directly within the string.

20. Destructuring Assignment

  • Destructuring assignment allows unpacking values from arrays or properties from objects into distinct variables, simplifying code and improving readability.

Example:

const user = { id: 1, name: "Bob", age: 30 };
const { name, age } = user;
console.log(name); // Output: Bob
console.log(age); // Output: 30
登录后复制

This example demonstrates how to extract properties from an object into individual variables, making the code cleaner and more concise.

Conclusion

Using these core JavaScript concepts, you will write scalable, efficient, and maintainable NodeJs applications. NodeJs is built on JavaScript's event-driven and asynchronous nature, so you should have a good grasp of these concepts at this point. Beyond these 20 points, the more you learn about Node.js feature changes and patterns, the better your NodeJs development skills will become.

以上是每个 Node 开发人员都应该掌握的基本 JavaScript 概念的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板