Annexe

Héritage des prototypes

À l'exception de null et undefined, chaque type de données primitives possède une prototype, un wrapper d'objet correspondant qui fournit des méthodes pour travailler avec des valeurs. Lorsqu'une recherche de méthode ou de propriété est appelée sur une primitive, JavaScript encapsule la primitive en arrière-plan et appelle la méthode ou effectue plutôt la recherche de propriétés sur l'objet wrapper.

Par exemple, un littéral de chaîne n'a pas de méthode propre, mais vous pouvez appeler la fonction Méthode .toUpperCase() dessus grâce à l'objet String correspondant wrapper:

"this is a string literal".toUpperCase();
> THIS IS A STRING LITERAL

C'est ce qu'on appelle l'héritage prototypique, qui consiste à hériter des propriétés et des méthodes. à partir du constructeur correspondant à une valeur.

Number.prototype
> Number { 0 }
>  constructor: function Number()
>  toExponential: function toExponential()
>  toFixed: function toFixed()
>  toLocaleString: function toLocaleString()
>  toPrecision: function toPrecision()
>  toString: function toString()
>  valueOf: function valueOf()
>  <prototype>: Object { … }

Vous pouvez créer des primitives à l'aide de ces constructeurs, au lieu de vous contenter de définir en fonction de leur valeur. Par exemple, l'utilisation du constructeur String crée un un objet de chaîne, et non un littéral de chaîne: un objet qui contient non seulement notre chaîne, mais toutes les propriétés et méthodes héritées du constructeur.

const myString = new String( "I'm a string." );

myString;
> String { "I'm a string." }

typeof myString;
> "object"

myString.valueOf();
> "I'm a string."

Pour la plupart, les objets résultants se comportent comme les valeurs que nous avons utilisées pour les définir. Par exemple, même si vous définissez une valeur numérique à l'aide de la méthode Le constructeur new Number génère un objet contenant toutes les méthodes et du prototype Number, vous pouvez utiliser des opérateurs mathématiques sur ces objets comme vous le feriez avec des littéraux numériques:

const numberOne = new Number(1);
const numberTwo = new Number(2);

numberOne;
> Number { 1 }

typeof numberOne;
> "object"

numberTwo;
> Number { 2 }

typeof numberTwo;
> "object"

numberOne + numberTwo;
> 3

Vous aurez très rarement besoin d'utiliser ces constructeurs, car JavaScript est intégré l'héritage prototypique signifie qu'ils n'apportent aucun avantage pratique. Création... Les primitives utilisant des constructeurs peuvent également entraîner des résultats inattendus, le résultat est un objet, et non un simple littéral:

let stringLiteral = "String literal."

typeof stringLiteral;
> "string"

let stringObject = new String( "String object." );

stringObject
> "object"

Cela peut compliquer l'utilisation d'opérateurs de comparaison stricts:

const myStringLiteral = "My string";
const myStringObject = new String( "My string" );

myStringLiteral === "My string";
> true

myStringObject === "My string";
> false

Insertion automatique de point-virgule (ASI)

Lors de l'analyse d'un script, les interpréteurs JavaScript peuvent utiliser une fonctionnalité appelée l'insertion automatique de points-virgules (ASI) pour essayer de corriger les instances ou des points-virgules. Si l'analyseur JavaScript rencontre un jeton non autorisé, il tente d'ajouter un point-virgule avant ce jeton pour corriger l'erreur de syntaxe potentielle, car tant qu'une ou plusieurs des conditions suivantes sont remplies:

  • Ce jeton est séparé du jeton précédent par un saut de ligne.
  • Ce jeton est }.
  • Le jeton précédent est ), et le point-virgule inséré correspond à la fin point-virgule d'une instruction do...while.

Pour en savoir plus, consultez les règles ASI.

Par exemple, le fait d'omettre des points-virgules après les instructions suivantes n'entraînera pas erreur de syntaxe à cause d'ASI:

const myVariable = 2
myVariable + 3
> 5

Toutefois, ASI ne peut pas prendre en compte plusieurs instructions sur la même ligne. Si vous rédigez plusieurs instructions sur la même ligne, en veillant à les séparer par des Points-virgules:

const myVariable = 2 myVariable + 3
> Uncaught SyntaxError: unexpected token: identifier

const myVariable = 2; myVariable + 3;
> 5

ASI est une tentative de correction d'erreur, et non une sorte de flexibilité syntaxique conçue en JavaScript. Assurez-vous d'utiliser des points-virgules lorsque cela est approprié afin de ne pas compter pour produire un code correct.

Mode strict

Les normes qui régissent l'écriture du code JavaScript ont bien évolué tout ce qui avait été pris en compte lors de la conception initiale du langage. Chaque nouvelle modification apportée à Le comportement attendu de JavaScript doit éviter de provoquer des erreurs sur les sites Web plus anciens.

ES5 résout certains problèmes de longue date liés à la sémantique JavaScript sans de casser les implémentations existantes en introduisant un "mode strict", un moyen d'opter dans un ensemble plus restrictif de règles de langage, soit pour l'intégralité d'un script, fonction individuelle. Pour activer le mode strict, utilisez le littéral de chaîne "use strict", suivi d'un point-virgule, sur la première ligne d'un script ou :

"use strict";
function myFunction() {
  "use strict";
}

Le mode strict empêche certaines données "non sécurisées" ou des fonctionnalités obsolètes, génère des erreurs explicites à la place des modes "silencieux" courants et interdit l'utilisation qui pourraient entrer en conflit avec de futures fonctionnalités linguistiques. Par exemple, début décisions de conception autour d'une portée variable les développeurs sont plus susceptibles de "polluer" par erreur le champ d'application global déclarer une variable, quel que soit le contexte qui la contient, en omettant le Mot clé var:

(function() {
  mySloppyGlobal = true;
}());

mySloppyGlobal;
> true

Les environnements d'exécution JavaScript modernes ne peuvent pas corriger ce comportement, sans risque endommager tout site Web qui s'appuie sur ce dernier, de manière erronée ou délibérée. Le JavaScript moderne l'empêche plutôt par les développeurs d'opter pour pour les nouvelles tâches, et l'activation du mode strict par défaut uniquement dans le contexte nouvelles fonctionnalités linguistiques qui n'affecteront pas les anciennes implémentations:

(function() {
    "use strict";
    mySloppyGlobal = true;
}());
> Uncaught ReferenceError: assignment to undeclared variable mySloppyGlobal

Vous devez écrire "use strict" en tant que littéral de chaîne. Un littéral de modèle (use strict) ne fonctionnera pas. Vous devez également inclure "use strict" avant tout code exécutable dans son contexte prévu. Sinon, l'interprète l'ignore.

(function() {
    "use strict";
    let myVariable = "String.";
    console.log( myVariable );
    sloppyGlobal = true;
}());
> "String."
> Uncaught ReferenceError: assignment to undeclared variable sloppyGlobal

(function() {
    let myVariable = "String.";
    "use strict";
    console.log( myVariable );
    sloppyGlobal = true;
}());
> "String." // Because there was code prior to "use strict", this variable still pollutes the global scope

Par référence, par valeur

Toute variable, y compris les propriétés d'un objet, paramètres de fonction et éléments d'une tableau, set ; ou map, peut contenir soit une primitive, ou une valeur de référence.

Lorsqu'une valeur primitive est attribuée d'une variable à une autre, le code JavaScript crée une copie de cette valeur et l'attribue à la variable.

Lorsque vous attribuez un objet (instances de classe, tableaux et fonctions) à une variable, au lieu de créer une copie de cet objet, celle-ci contient une à la position stockée en mémoire de l'objet. Pour cette raison, la modification Un objet référencé par une variable modifie l'objet référencé, et pas seulement une valeur contenue dans cette variable. Par exemple, si vous initialisez un nouveau par une variable contenant une référence d'objet, puis utilisez la nouvelle variable pour ajouter une propriété à cet objet, la propriété et sa valeur sont ajoutées à l'objet d'origine:

const myObject = {};
const myObjectReference = myObject;

myObjectReference.myProperty = true;

myObject;
> Object { myProperty: true }

Ceci est important non seulement pour modifier des objets, mais aussi pour effectuer comparaisons, car une égalité stricte entre les objets exige que les deux variables faire référence au même objet pour renvoyer la valeur true. Ils ne peuvent pas faire référence objets différents, même s'ils sont structurellement identiques:

const myObject = {};
const myReferencedObject = myObject;
const myNewObject = {};

myObject === myNewObject;
> false

myObject === myReferencedObject;
> true

Allocation de mémoire

JavaScript utilise la gestion automatique de la mémoire, ce qui signifie que la mémoire n'a pas besoin explicitement allouées ou libérées pendant le développement. Alors que les détails des moteurs JavaScript approches de la gestion de la mémoire vont au-delà le champ d'application de ce module, c'est-à-dire comprendre comment la mémoire est allouée le contexte pour travailler avec des valeurs de référence.

Il y a deux « zones » en mémoire: la "pile" et le "tas". La pile stocke des données statiques (valeurs primitives et références aux objets), car le l'espace requis pour stocker ces données peut être alloué avant que le s'exécute. Le tas de mémoire stocke des objets, qui nécessitent un espace alloué dynamiquement car leur taille peut changer lors de l'exécution. La mémoire est libérée par un processus appelée "récupération de mémoire", ce qui supprime les objets sans référence mémoire.

Thread principal

JavaScript est un langage fondamentalement monothread avec une approche "synchrone" modèle d'exécution, ce qui signifie qu'il ne peut exécuter qu'une seule tâche à la fois. Ce contexte d'exécution séquentielle est appelé thread principal.

Le thread principal est partagé par d'autres tâches du navigateur, comme l'analyse HTML, le rendu de certaines parties de la page, l'exécution d'animations CSS la gestion des interactions des utilisateurs, des plus simples (comme la mise en surbrillance de texte) à le complexe (comme interagir avec des éléments du formulaire). Les fournisseurs de navigateurs ont trouvé des moyens d'optimiser les tâches effectuées par le thread principal, mais des tâches plus complexes les scripts peuvent encore utiliser une trop grande partie des ressources du thread principal et avoir un impact global les performances de la page.

Certaines tâches peuvent être exécutées d'arrière-plan appelés nœuds de calcul Web, avec quelques restrictions:

  • Les threads de calcul ne peuvent agir que sur des fichiers JavaScript autonomes.
  • L'accès à la fenêtre et à l'interface utilisateur du navigateur a été considérablement réduit, voire inexistant.
  • Leur communication avec le thread principal est limitée.

Ces limitations en font un outil idéal pour les tâches ciblées et gourmandes en ressources pourrait occuper le thread principal.

Pile d'appel

La structure de données utilisée pour gérer les « contextes d’exécution », le code étant activement exécutées : il s'agit d'une liste appelée pile d'appel (généralement juste "la pile"). Lorsqu'un script est exécuté pour la première fois, l'interpréteur JavaScript crée un "contexte d'exécution globale" et le transfère dans la pile d'appel, les instructions de ce contexte global, exécutées une par une, de haut en bas en bas. Lorsque l'interpréteur rencontre un appel de fonction lors de l'exécution de la global, il transmet un "contexte d'exécution de la fonction" pour cet appel en haut de la pile, met en pause le contexte d'exécution global et exécute la fonction le contexte d'exécution.

Chaque fois qu'une fonction est appelée, le contexte d'exécution de cette fonction est le suivant : en haut de la pile, juste au-dessus du contexte d'exécution actuel. La pile d'appel opère sur un principe de type "premier entré, premier sorti" ce qui signifie que les plus l'appel de fonction récent, qui a le plus haut dans la pile, est exécuté et continue jusqu'à ce qu'il soit résolu. Lorsque cette fonction est terminée, l'interpréteur la supprime de la pile d'appel et le contexte d'exécution contenant cet appel de fonction redevient l'élément le plus élevé de la pile et reprend l'exécution.

Ces contextes d'exécution capturent toutes les valeurs nécessaires à leur exécution. Ils établissent également les variables et les fonctions disponibles dans le cadre en fonction de son contexte parent, puis déterminez et définissez la valeur de la Mot clé this dans le contexte de la fonction.

Boucle d'événements et file d'attente de rappel

Cette exécution séquentielle signifie que les tâches asynchrones qui incluent des rappels comme extraire des données d'un serveur, répondre à une interaction de l'utilisateur, ou que vous attendiez un minuteur défini sur setTimeout ou setInterval, soit bloqué le thread principal jusqu'à ce que cette tâche soit terminée ou qu'il interrompe de manière inattendue contexte d'exécution actuel au moment où le contexte d'exécution de la fonction de rappel est ajoutée à la pile. Pour résoudre ce problème, JavaScript gère les tâches asynchrones à l'aide d'un "modèle de simultanéité" basé sur les événements composée de la "boucle d'événements" et "file d'attente de rappel" (parfois appelée "file d'attente de messages").

Lorsqu'une tâche asynchrone est exécutée sur le thread principal, le rappel le contexte d'exécution de la fonction est placé dans la file d'attente de rappel, et non au-dessus de pile d'appel. La boucle d'événements est un modèle parfois appelé réacteur, qui, en continu, interroge l'état de la pile d'appel et de la file d'attente de rappel. S'il y a des tâches dans la file d'attente de rappel et la boucle d'événements déterminent que la pile d'appel est vide, les tâches de la file d'attente de rappel sont transférées une par une vers la pile exécuté.