Bienvenue dans notre série d’articles : Javascript : sous le capot ! Le but de cette série est d’en apprendre un peu plus sur Javascript et de comprendre comment ça fonctionne réellement ! On démarre par un débat souvent animé : Javascript, un langage compilé ou interprété ?
Malgré le fait que le Javascript soit considéré dans l’imaginaire collectif comme un langage dynamique... Javascript est en fait un langage compilé !
Effectivement, Javascript n’est pas compilé autant à l’avance que des langages plus classiques comme le Java ou le .NET par exemple.
Mais, il n’en reste pas moins que le moteur JS performe quasiment les mêmes étapes (si ce n’est plus) que lors d’une compilation d’un langage compilé classique.
Le plus grand enjeu de la compilation du Javascript vient du fait qu’elle se fait immédiatement avant l’exécution.
Elle n’a pas la chance d’avoir tout le temps qu’il lui faut pour build un exécutable type jar ou dll, qui lui permettrait d’optimiser tout au maximum.
var a = 1
;
Lors de cette étape, les tokens suivants seront générés : var
, a
, =
, 1
, ;
.A partir de maintenant, nous allons parler de 3 acteurs différents :
Quand on voit le code var a = 1;
, on pense le plus souvent qu’il y a ici une seule action.
Seulement, ce n’est pas comme ça que le Moteur voit les choses ! En effet, il voit 1 action effectuée au moment de la compilation, et 1 autre, au moment de l’exécution. (En dehors de la tokenisation)
Essayons de décomposer ce code :
var a
, le Compilateur demande au Scope de regarder si la variable a
existe déjà dans le scope en question.a
dans le scope en question.a = 2
qui sera exécuté par le Moteur plus tard : Le Moteur demandera au Scope si a
existe.Pour rentrer encore plus en profondeur dans la compréhension, nous allons devoir introduire de nouveaux termes : LHS et RHS, Left-Hand Side et Right-Hand Side.
Quand le Moteur doit exécuter le code généré par le Compilateur, il va rechercher la variable a
pour voir si elle est déclarée, et pour cela, il va demander au Scope.
Mais, il existe plusieurs types de recherche, et le résultat dépendra du type.
Dans notre cas précédent, c’était une recherche LHS de la variable a
.
Pour simplifier la compréhension, RHS peut être également vu comme «Retrieve His Source», dans le sens où on veut la valeur de la variable en question.
On peut donc voir LHS comme « le reste », comme récupérer le conteneur de la variable en question au lieu de son contenu.
Quand on écrit :
console.log(a);
C’est donc une recherche RHS, puisqu’on veut connaitre la valeur de a
pour la passer à console.log
Tandis qu’au contraire quand on a :
a = 2;
C’est une recherche LHS, car nous ne sommes pas intéressés par la valeur de a
. On veut simplement son conteneur pour lui assigner la valeur 2.
On disait jusqu’à maintenant que le Scope gardait une liste des variables déclarées. Seulement, il peut y avoir plusieurs Scopes.
De la même manière que les fonctions, les Scopes sont imbriqués les uns dans les autres. Si une variable ne peut pas être trouvée dans le scope immédiat,
le Moteur consulte le scope du « dessus ». Tant qu’il ne trouve pas la variable, il continue à remonter jusque tout en haut (scope dit « global »).
Imaginons le programme suivant :
function foo(a) {
console.log(a + b);
}
var b = 2;
foo(2); // 4
La RHS de b
ne peut pas être résolue dans le Scope de foo
, mais il peut l’être dans le Scope du dessus (en l’occurrence ici le Scope global).
Le Moteur va tout d’abord demander au Scope de foo
, pour une RHS de b
.
Le Scope va lui dire qu’il ne l’a pas, le Moteur va donc demander au Scope du dessus qui lui, va lui renvoyer la valeur souhaitée.
Il est tentant de penser qu’une déclaration de fonction est une LHS. En effet, on pourrait imaginer décomposer la déclaration de function foo(a) {}
en var foo
puis foo = function(a){}
.
Cependant, ce n’est pas le cas. Pour des soucis de performance, les déclarations et assignations de fonctions sont faites au moment de la compilation. On ne peut donc pas vraiment parler de recherche LHS.
En fonction du type de recherche, le comportement sera différent dans le cas où elle échoue.
function foo(a) {
console.log(a + b);
b = a;
}
foo(2);
Quand la RHS est fait sur b
, rien ne sera trouvé car la variable n’existe pas. Si une RHS échoue à trouver une variable, le résultat sera une erreur ReferenceError retourné par le Moteur.
A contrario, si une LHS échoue, et si le programme n’est pas en « Strict Mode », Scope va s’occuper de créer un conteneur pour la variable en question, et le retourner au Moteur.
Strict Mode a été ajouté dans ES5, et permet de rajouter des règles au JS qui ne sont pas présentes de base. L’une d’elle empêchant la création automatique de variable implicite.
Dans ce cas-là, une LHS qui échoue renverra une ReferenceError.
Prenons un exemple un peu complet que nous pouvons décomposer pour voir toutes les recherches LHS et RHS qui sont faites (n’hésitez pas à chercher par vous-même avant de voir les réponses !).
function foo(a) {
var b = a;
return a + b;
}
var c = foo(2);
Il y a ici 3 LHS et 4 RHS.
LHS :
RHS :
Cette première partie de notre série Javascript sous le capot est maintenant terminée ! Pour aller plus loin, voici les articles suivants :
Le scope en Javascript : définition et explications.
Aujourd’hui, nous allons nous concentrer sur un sujet assez méconnu et pour le moins complexe : l’envoi des emails. Vous allez me dire que tout le monde arrive à envoyer des emails, et c’est bien vrai ! Mais pouvez-vous tous assurer que
Implémentation de Retrofit dans un projet Android avec Coroutine