De la définition à l'exécution, le moteur JS fait beaucoup de travail d'initialisation dans la couche d'implémentation. Par conséquent, avant d'apprendre le mécanisme de travail du moteur JS, nous devons introduire plusieurs concepts connexes: pile d'environnement d'exécution, objets globaux, environnement d'exécution, objets variables, objets actifs, chaînes de portée et de portée, etc. Ces concepts sont les composants principaux du travail du moteur JS. Le but de cet article n'est pas de vous expliquer chaque concept de manière isolée, mais de l'analyser à travers une simple démo, expliquant les détails du moteur JS de la définition à l'exécution, et le rôle que ces concepts y jouent.
var x = 1; // définir une variable globale xfunction a (y) {var x = 2; // définir une variable locale x fonction b (z) {// définir une fonction interne b console.log (x + y + z); } return b; // renvoie une référence à la fonction b} var c = a (1); // Exécuter A, retour BC (1); // Exécuter la fonction BCette démo est une fermeture, et le résultat d'exécution est 4. Ci-dessous, nous analyserons le mécanisme de travail du moteur JS en trois étapes: l'initialisation globale, la fonction d'exécution A et la fonction d'exécution B :
1. Initialisation globale
Lorsque le moteur JS entre dans un morceau de code exécutable, il doit effectuer les trois tâches d'initialisation suivantes:
Tout d'abord, créez un objet global (objet global). Il n'y a qu'une seule copie globale de cet objet, ses propriétés sont accessibles n'importe où et son existence accompagnera l'ensemble du cycle de vie de l'application. Lors de la création d'un objet global, des objets JS couramment utilisés tels que les mathématiques, la chaîne, la date, le document sont utilisés comme propriétés. Étant donné que cet objet global ne peut pas être accessible directement par son nom, il existe une autre fenêtre de propriété, et il pointe la fenêtre vers lui-même, de sorte que l'objet global peut être accessible via la fenêtre. La structure générale de l'utilisation du pseudo-code pour simuler les objets globaux est le suivant:
// Créer un objet global var globalObject = {math: {}, string: {}, date: {}, document: {}, // Dom Operation ... Window: this // Laissez l'attribut de fenêtre pointer à lui-même}Ensuite, le moteur JS doit créer une pile de contexte d'exécution. Dans le même temps, il doit également créer un contexte d'exécution global EC et pousser cet environnement d'exécution global EC dans la pile d'environnement d'exécution. La fonction de la pile d'environnement d'exécution est de s'assurer que le programme peut être exécuté dans le bon ordre. Dans JavaScript, chaque fonction a son propre environnement d'exécution. Lors de l'exécution d'une fonction, l'environnement d'exécution de la fonction sera poussé vers le haut de la pile d'environnement d'exécution et obtiendra des droits d'exécution. Lorsque cette fonction est exécutée, son environnement d'exécution est supprimé du haut de la pile et le droit d'exécution est renvoyé à l'environnement d'exécution précédent. Nous utilisons Pseudocode pour simuler la relation entre la pile d'environnement d'exécution et EC:
var Ecstack = []; // Définir une pile d'environnement d'exécution, similaire à Array var ec = {}; // Créer un espace d'exécution, // La spécification ECMA-262 ne définit pas clairement la structure de données de l'EC, vous pouvez la comprendre comme un élément d'espace alloué dans la mémoire Ecstack.push (EC); // Entrez la fonction et poussez l'environnement d'exécution Ecstack.pop (EC); // Une fois la fonction renvoyée, supprimez l'environnement d'exécutionEnfin, le moteur JS crée également un objet variable global (objet Varibale) VO associé à EC et pointe VO VO à l'objet global. VO contient non seulement les propriétés d'origine de l'objet global, mais inclut également la variable X et la fonction a définie à l'échelle mondiale. Dans le même temps, lors de la définition de la fonction A, une portée d'attribut interne est également ajoutée à A et des points de points à VO. Lorsque chaque fonction est définie, un attribut de portée qui y est associé sera créé et la portée pointe toujours vers l'environnement dans lequel la fonction est définie. La structure de l'extack pour le moment est la suivante:
Ecstack = [// Environnement d'exécution Stack EC (G) = {// Environnement d'exécution global VO (G): {// Définir l'objet de variable global ... // Contient les attributs d'origine de l'objet global x = 1; // définir la variable x a = function () {...}; // définir la fonction aa [[SPOPE]] = this; // définir la portée de A et attribuer une valeur à VO elle-même}}];2. Exécuter la fonction a
Lorsque l'exécution entre un (1), le moteur JS doit effectuer ce qui suit:
Tout d'abord, le moteur JS créera l'environnement d'exécution EC de la fonction A, puis l'EC le poussera en haut de la pile d'environnement d'exécution et obtiendra les droits d'exécution. À l'heure actuelle, il existe deux environnements d'exécution dans la pile d'environnement d'exécution, à savoir l'environnement d'exécution global et la fonction dans un environnement d'exécution. L'environnement d'exécution de A est en haut de la pile, et l'environnement d'exécution globale est en bas de la pile. Ensuite, créez la chaîne de fonction de la fonction A. En JavaScript, chaque environnement d'exécution a sa propre chaîne de portée pour la résolution d'identifiant. Lorsque l'environnement d'exécution est créé, sa chaîne de portée est initialisée comme l'objet contenu dans la portée de la fonction en cours d'exécution.
Ensuite, le moteur JS créera un objet actif (objet d'activation) de la fonction actuelle. L'objet actif joue ici le rôle d'un objet variable, mais il est appelé différemment dans la fonction (vous pouvez penser qu'un objet variable est un concept général, et l'objet actif en est une branche). AO contient les paramètres formels de la fonction, les arguments objet, cet objet, ainsi que les définitions des variables locales et des fonctions internes, puis AO sera poussée au sommet de la chaîne de portée. Il convient de noter que lors de la définition de la fonction B, le moteur JS ajoutera également un attribut de portée à B et la portée ponctuelle à l'environnement où la fonction B est définie. L'environnement où la fonction B est définie est l'objet actif de A, et AO est situé à l'avant de la liste liée. Étant donné que la liste liée a les caractéristiques de la connexion finale, la portée de la fonction B pointe toute la chaîne de portée de A. Jetons un coup d'œil à la structure de l'extack à ce moment:
Ecstack = [// Environnement d'exécution Stack EC (A) = {// AIMENTATION D'EXÉCUTION A [SPOCE]: VO (G), // VO est l'objet variable global ao (a): {// Créer l'objet actif y: 1, x: 2, // définir la variable locale x b: function () {...}, // définir la fonction bb [SCOPE]] = this; // Cela fait référence à AO lui-même, et AO est en haut de Scopechain, donc b [[SCOPE]] pointe vers l'ensemble des arguments de la chaîne de portée: [], // Les arguments auxquels nous accédons dans la fonction sont des arguments dans AO ceci: Window // Ceci dans la fonction Points à l'objectif de la fenêtre de l'appelant}, Scopechain: <ao (A), [Scope]]>> // La liste liée est initiale comme a [SCOPE], et Scope]]>> // La liste liée est Initial As A [Scope], et la Scope]]>> Ensuite, AO est ajouté au sommet de la chaîne de portée. À l'heure actuelle, la chaîne de portée de A: ao (a) -> vo (g)}, ec (g) = {// Environnement d'exécution global VO (g): {// Créer un objet variable global ... // Contient les attributs originaux de l'objet global x = 1; // définir la variable x a = function () {...}; // Définissez la fonction aa [[SCOPE]] = this; // Définir la portée de A, A [[SCOPE]] == VO (G)}}];3. Exécuter la fonction B
Une fois la fonction a exécutée, la référence à B est renvoyée et attribuée à la variable C. L'exécution de C (1) est équivalente à l'exécution B (1). Le moteur JS doit effectuer les tâches suivantes:
Tout d'abord, comme ci-dessus, créez l'environnement d'exécution EC de la fonction B, puis poussez l'EC vers le haut de la pile d'environnement d'exécution et obtenez les droits d'exécution. À l'heure actuelle, il existe deux environnements d'exécution dans la pile d'environnement d'exécution, à savoir l'environnement d'exécution global et l'environnement d'exécution de la fonction B. L'environnement d'exécution de B est en haut de la pile, et l'environnement d'exécution globale est au bas de la pile. (Remarque: Lorsque la fonction A revient, l'environnement d'exécution de A sera supprimé de la pile, ne laissant que l'environnement d'exécution global) puis, créez la chaîne de portée de la fonction B et l'initialisez-la dans l'objet contenu dans la portée de la fonction B, c'est-à-dire la chaîne de portée de A. Enfin, créez l'objet actif AO de la fonction B, et utilisez les paramètres z, les arguments objet et cet objet de B en tant que properties de Ao. À l'heure actuelle, EXTACK deviendra comme ceci:
Ecstack = [// Environnement d'exécution Stack ec (b) = {// Créer un environnement d'exécution de B et se trouve en haut de la chaîne de portée [Scope]: ao (a), // pointer la chaîne de portée de la fonction A, ao (a) -> vo (g) var ao (b) = {// Créer l'objet actif de la fonction b z: 1, arguments: [], cette: la fenêtre} Scopechain: <ao (b), b [[scope]]> // La liste liée est initialisée à B [[SCOPE]], puis AO (B) est ajouté à l'en-tête de la liste liée. À l'heure actuelle, la chaîne de portée de B: ao (b) -> ao (a) -vo (g)}, ec (a), // a l'environnement d'exécution de A a été supprimée du haut de la pile, ec (g) = {// l'environnement d'exécution globale VO: {// définir un objet variable global ... // contient les attributs originaux de l'objet global x = 1; // définir la variable x a = function () {...}; // définir la fonction aa [[SPOPE]] = this; // Définir la portée de A, A [[SCOPE]] == VO (G)}}];Lorsque la fonction B exécute "x + y + z", il est nécessaire d'analyser les trois identifiants x, y et z un par un. Le processus d'analyse adhère aux règles de recherche de variables: trouvez d'abord si l'attribut existe dans votre objet actif. S'il existe, arrêtez de chercher et de retour; S'il n'existe pas, continuez à rechercher du sommet le long de sa chaîne de portée, jusqu'à ce qu'il soit trouvé. Si la variable n'est pas trouvée sur toute la chaîne de portée, retournez "Undefined". D'après l'analyse ci-dessus, nous pouvons voir que la chaîne de portée de la fonction B est la suivante:
Ao (b) -> ao (a) -> vo (g)
Par conséquent, la variable X se trouvera dans AO (a), et ne recherchera pas x dans Vo (g), la variable y se trouve dans AO (a), et la variable Z se trouve dans son propre AO (b). Ainsi, le résultat de l'exécution: 2 + 1 + 1 = 4.
Résumé simple
Après avoir compris le mécanisme de travail du moteur JS, nous ne pouvons pas simplement rester au niveau de la compréhension du concept, mais l'utiliser comme un outil de base pour optimiser et améliorer notre code dans le travail réel, améliorer l'efficacité de l'exécution et générer une valeur réelle. Prenez le mécanisme de recherche variable comme exemple. Si votre code est profondément imbriqué et chaque fois que vous vous référez à une variable globale, le moteur JS recherchera toute la chaîne de portée. Par exemple, il y a ce problème avec des objets de fenêtre et de document au bas de la chaîne de portée. Par conséquent, nous pouvons faire beaucoup d'optimisation des performances sur ce problème, et bien sûr, il existe d'autres aspects des optimisations. Je n'entrerai pas dans les détails ici. Cet article est juste considéré comme un avertissement!
par @ 一竞 2015
Ce qui précède est tout le contenu de cet article. J'espère que cela sera utile à l'apprentissage de tous et j'espère que tout le monde soutiendra davantage Wulin.com.