Variables, portée et mémoire en JavaScript

En JavaScript, les variables peuvent contenir des valeurs primitives ou des valeurs par référence. Les valeurs primitives incluent Undefined, Null, Number, String, Boolean et Symbol, et sont accédées par valeur, signifiant que l'opération porte directement sur la valeur stockée. Les valeurs par référence sont des objets stockés en mémoire, et les variables les contenant sont accédées par référence, c'est-à-dire que l'opération agit sur la référence, pas sur l'objet lui-même.

Lors de la copie d'une valeur primitive, la valeur est dupliquée et les variables deviennent indépendantes. Pour les valeurs par référence, seule la référence est copiée, donc les deux variables pointent vers le même objet, et toute modification est partagée. Tous les paramètres de fonction en JavaScript sont passés par valeur, comme illustré ci-dessous :

function changerNom(param) {
    param.nom = "Jean";
    param = new Object();
    param.nom = "Pierre";
}

let objet = new Object();
changerNom(objet);
console.log(objet.nom); // "Jean"

Pour vérifier le type d'un objet, on utilise l'opérateur instanceof. Par exemple :

let individu = new String();
console.log(individu instanceof Object); // true
console.log(individu instanceof Number); // false
console.log(individu instanceof String); // true

Le contexte d'exécution est l'environnement où le code JavaScript est interprété. Chaque contexte possède un objet varible qui stocke les variables et fonctions définies. Le contexte global, correspondant à l'objet window dans un navigateur, contient les variables globales déclarées avec var. Les fonctions créent leurs propres contextes, empilés sur une pile d'exécution, et une chaîne de portée permet de chercher les identifiants dans les contextes supérieurs.

Les déclarations avec var sont ignorées si répétées, tandis que let lève une erreur. const nécessite une initialisation et empêche la réaffectation, mais les objets const peuvent avoir leurs propriétés modifiées, sauf s'ils sont gelés avec Object.freeze.

const element = Object.freeze({});
element.nom = "Marc";
console.log(element.nom); // undefined

Exemple de recherche d'identifiant dans la portée :

var teinte = 'bleu';
function recupererTeinte() {
    let teinte = 'rouge';
    let nuance = 'noir';
    {
        let teinte = 'vert';
        return teinte;
    }
}
console.log(recupererTeinte()); // 'vert'

JavaScript utilise un ramasse-miettes pour gérer la mémoire automatiquement. Les stratégies courantes incluent le marquage-compaction, où les variables actives sont préservées et les autres supprimées, et le comptage de références, qui libère les valeurs avec zéro référence. Pour optimiser la mémoire, il est conseillé de déréférencer les variables inutilisées en les mettant à null.

function fabriquerPersonne(nom) {
    let personneLoc = new Object();
    personneLoc.nom = nom;
    return personneLoc;
}

let personneGlob = fabriquerPersonne("Nicolas");
personneGlob = null; // déréférencement

D'autres techniques pour améliorer les performances incluent l'utilisation de const et let, l'évitement des suppressions dynamiques de propriétés, et l'implémentation de pools d'objets pour réduire la fréquence du ramasse-miettes. Par exemple, définir toutes les propriétés dans le constructeur évite les classes cachées différentes :

function Document(auteur) {
    this.titre = 'test';
    this.auteur = auteur;
}
let doc1 = new Document();
let doc2 = new Document('Sophie');

// Mauvaise pratique : suppression dynamique
delete doc1.auteur; // à éviter

// Bonne pratique : déréférencement
doc1.auteur = null;

La gestion efficace de la mémoire est essentielle pour les applications web performantes, en particulier sur les plateformes avec des ressources limitées.

Étiquettes: JavaScript variables portée mémoire ramasse-miettes

Publié le 18 juin à 02h26