Analyse d'un exemple de code du mécanisme d'exécution JavaScript
Commençons par une question simple :
<script type="text/javascript"> alert(i); var i = 1; </script>
Le le résultat de sortie n'est pas défini. Ce phénomène est appelé « pré-analyse » : le moteur JavaScript analysera d'abord les variables var et les définitions de fonctions. Le code n’est exécuté qu’une fois la pré-analyse terminée. Si un flux de document contient plusieurs segments de code de script (code js séparé par des balises de script ou des fichiers js importés
étape 1 Lisez la première section de code
étape 2). . Effectuez une analyse syntaxique. S'il y a une erreur, une erreur de syntaxe sera signalée (comme des crochets incompatibles, etc.) et passez à l'étape 5
étape 3. Effectuez une "pré-analyse" des variables var et des définitions de fonctions (non). les erreurs seront jamais signalées) Oui, car seules les déclarations correctes sont analysées)
étape 4 Exécutez le segment de code et signalez une erreur s'il y a une erreur (par exemple, la variable n'est pas définie)
étape 5. est un autre segment de code, lisez le segment de code suivant et répétez l'étape 2
étape 6. Fin
L'analyse ci-dessus peut déjà expliquer de nombreux problèmes, mais j'ai toujours l'impression qu'il manque quelque chose. Par exemple, à l'étape 3, qu'est-ce que la « pré-analyse » ? Et à l'étape 4, regardez l'exemple suivant :
<script type="text/javascript"> alert(i); // error: i is not defined. i = 1; </script>
Pourquoi la première phrase provoque-t-elle une erreur ? En JavaScript, les variables n’ont-elles pas besoin d’être indéfinies ?
Le temps passe comme un cheval blanc, à côté de la bibliothèque, j'ouvre "Principes de compilation" comme si c'était un monde à part. Il y a une telle note dans le familier. espace vide encore inconnu :
Pour les langages compilés traditionnels, les étapes de compilation sont divisées en : analyse lexicale, analyse syntaxique, vérification sémantique, optimisation du code et génération d'octets.
Mais pour les langages interprétés, une fois l'arbre syntaxique obtenu grâce à l'analyse lexicale et à l'analyse syntaxique, l'interprétation et l'exécution peuvent commencer.
En termes simples, l'analyse lexicale consiste à convertir le flux de caractères (flux de caractères) en un flux de jetons (flux de jetons), par exemple en convertissant c = a – b en :
NAME "c" EQUALS NAME "a" MINUS NAME "b" SEMICOLON
Ci-dessus, ceci n'est qu'un exemple. Pour plus d'informations, veuillez consulter l'analyse lexicale.
Le chapitre 2 du « Guide définitif de JavaScript » parle de la structure lexicale, qui est également décrite dans l'ECMA-262. La structure lexicale est la base d’une langue et est facile à maîtriser. Quant à la mise en œuvre de l’analyse lexicale, c’est un autre domaine de recherche qui ne sera pas exploré ici.
Vous pouvez utiliser le langage naturel comme analogie. L'analyse lexicale est une traduction simple. Par exemple, si un paragraphe d'anglais est traduit mot par mot en chinois, vous obtenez un tas de mots. flux de jetons, ce qui est difficile à comprendre. Une traduction plus approfondie nécessite une analyse grammaticale. La figure suivante est un arbre syntaxique d'une instruction conditionnelle :
Lors de la construction de l'arbre syntaxique, s'il s'avère qu'il ne peut pas être construit, par exemple if(a { i = 2; }, cela signalera une erreur de syntaxe et mettra fin à l'analyse de l'intégralité du bloc de code. Il s'agit de l'étape 2 au début de cet article
Après avoir construit un arbre syntaxique grâce à l'analyse syntaxique, les phrases traduites peuvent toujours l'être. ambiguë. Ensuite, une vérification sémantique supplémentaire est nécessaire. Pour les langages fortement typés traditionnels, la partie principale de la vérification sémantique est la vérification de type, par exemple si les types de paramètres réels et les types de paramètres formels de la fonction correspondent. Pour les langages faiblement typés, cette étape peut ne pas être effectuée. être disponible (j'ai une énergie limitée et je n'ai pas le temps de regarder l'implémentation du moteur JS. Je ne sais pas s'il y a une étape de vérification sémantique dans le moteur JS
Cela peut être vu depuis le). ci-dessus, pour le moteur JavaScript, il doit y avoir une analyse lexicale et une analyse syntaxique, suivies d'étapes telles que la vérification sémantique et l'optimisation du code (tout langage a un processus de compilation, mais les langages interprétés ne le sont pas). compilé en code binaire), l'exécution du code commencera
Le processus de compilation ci-dessus ne peut toujours pas expliquer plus en profondeur la "pré-analyse" au début de l'article. le code JavaScript Processus d'exécutionsemaines. Aimin en a une analyse très minutieuse dans la deuxième partie de "L'essence du langage JavaScript et de la pratique de la programmation". Grâce à la compilation, le code JavaScript a été traduit dans un arbre de syntaxe, puis il sera immédiatement exécuté selon l'arbre de syntaxe Pour une exécution ultérieure, vous devez comprendre le mécanisme de portée. de JavaScript. JavaScript utilise la portée lexicale. En termes simples, la portée des variables JavaScript est définie au moment de l'exécution plutôt qu'au moment de l'exécution, ce qui signifie que la portée lexicale dépend du code source et peut être déterminée par. le compilateur via une analyse statique. Par conséquent, la portée lexicale est également appelée portée statique (portée statique). Cependant, vous devez faire attention à la sémantique de with et eval. Cela ne peut pas être réalisé uniquement grâce à la technologie statique. il faut seulement dire que le mécanisme de portée de JS est très proche de la portée lexicale.Le moteur JS crée un contexte d'exécution lors de l'exécution de chaque instance de fonction. Le contexte contient un objet d'appel. L'objet d'appel est une structure scriptObject, qui est utilisé pour enregistrer la table de variables internes varDecls, la table de fonctions intégrée funDecls, la liste de références parent upvalue et d'autres structures d'analyse syntaxique (remarque : varDecls et funDecls et d'autres informations sont obtenues lors de l'étape d'analyse syntaxique et enregistrées dans le arbre syntaxique. Lorsque l'instance de fonction est exécutée, ces informations seront copiées de l'arborescence syntaxique vers le scriptObject). scriptObject est un ensemble de systèmes statiques liés aux fonctions, cohérents avec le cycle de vie des instances de fonction.lexical scope是JS的作用域机制,还需要理解它的实现方法,这就是作用域链(scope chain)。scope chain是一个name lookup机制,首先在当前执行环境的scriptObject中寻找,没找到,则顺着upvalue到父级scriptObject中寻找,一直 lookup到全局调用对象(global object)。
当一个函数实例执行时,会创建或关联到一个闭包(closure)。 scriptObject用来静态保存与函数相关的变量表,closure则在执行期动态保存这些变量表及其运行值。closure的生命周期有可能比函数实例长。函数实例在活动引用为空后会自动销毁,closure则要等要数据引用为空后,由JS引擎回收(有些情况下不会自动回收,就导致了内存泄漏)。
别被上面的一堆名词吓住,一旦理解了执行环境、调用对象、闭包、词法作用域、作用域链这些概念,JS语言的很多现象都能迎刃而解。
至此,对于文章开头部分的疑问,可以解释得很清楚了:
step3中所谓的“预解析”,其实是在step2的语法分析阶段完成,并存储在语法树中。当执行到函数实例时,会将varDelcs和funcDecls从语法树中复制到执行环境的scriptObject上。
step4中,未定义变量意味着在scriptObject的变量表中找不到,JS引擎会沿着scriptObject的upvalue往上寻找,如果都没找到,对于写操作i = 1; 最后就会等价为 window.i = 1; 给window对象新增了一个属性。对于读操作,如果一直追溯到全局执行环境的scriptObject上都找不到,就会产生运行期错误。
理解后,雾散花开,天空一片晴朗。
最后,留个问题给大家:
<script type="text/javascript"> var arg = 1; function foo(arg) { alert(arg); var arg = 2; } foo(3); </script>
请问alert的输出是什么?
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!