Maison  >  Article  >  interface Web  >  Explication détaillée du fonctionnement de JavaScript

Explication détaillée du fonctionnement de JavaScript

青灯夜游
青灯夜游avant
2019-11-26 15:24:472452parcourir

Explication détaillée du fonctionnement de JavaScript

Qu'est-ce que JavaScript ?

Confirmons la définition de JavaScript : JavaScript est un langage dynamique interprété.

Les langages interprétés existent par rapport aux langages compilés. Le code source n'est pas directement compilé en code cible, mais converti en code intermédiaire, qui est ensuite interprété et exécuté par l'interpréteur.

Les langages de programmation grand public incluent les langages compilés (comme C++), interprétés (comme JavaScript) et semi-interprétés et semi-compilés (comme Java).

[Recommandations de cours associées : Tutoriel vidéo JavaScript]

Comment le code s'exécute-t-il ?

Tout d’abord, comprenons comment le code s’exécute.

Nous savons que le code est exécuté par le CPU et que le CPU actuel ne peut pas exécuter directement des instructions telles que if…else, il ne peut exécuter que des instructions binaires. Mais les instructions binaires sont vraiment hostiles aux humains : il nous est difficile de déterminer rapidement et précisément ce que représente une instruction binaire 1000010010101001 ? Les scientifiques ont donc inventé le langage assembleur.

Langage assembleur

Le langage assembleur est en fait un mnémonique pour les instructions binaires.

Supposons que 10101010 représente une opération de lecture de mémoire, que l'adresse mémoire est 10101111 et que l'adresse de registre est 11111010, alors l'opération complète 101010101010111111111010 représente la lecture de la valeur d'une certaine adresse mémoire et le charger dans les registres, et le langage assembleur ne change pas ce mode de fonctionnement, c'est juste un mappage d'instructions binaires :

LD:10101010 
id:10101111
R:11111010

De cette façon, les instructions ci-dessus peuvent être exprimées comme LD id R, ce qui grandement améliore la lisibilité du code.

Mais ce n'est pas assez convivial. Le processeur ne peut exécuter que des expressions à trois adresses, ce qui est loin de la pensée humaine et des modèles de langage. De grands scientifiques ont donc inventé des langages de haut niveau.

Langage de haut niveau

« Le code est écrit pour que les gens le lisent, pas pour que les machines le lisent, c'est juste que l'ordinateur peut l'exécuter "

La raison pour laquelle le langage de haut niveau est appelé « haut niveau » est qu'il correspond davantage à nos habitudes de pensée et de lecture. if…else Ce genre de phrase semble bien plus confortable que 1010101010. Cependant, les ordinateurs ne peuvent pas exécuter directement des langages de haut niveau, ils doivent donc convertir les langages de haut niveau en langage assembleur/instructions machine avant de pouvoir être exécutés. Ce processus est une compilation.

JavaScript doit-il être compilé ?

JavaScript est sans aucun doute un langage de haut niveau, il doit donc absolument être compilé avant de pouvoir être exécuté. Mais pourquoi parle-t-on de langage interprété ? Quelle est la différence entre lui et les langages compilés et les langages semi-interprétés et semi-compilés ? Commençons par la compilation.

Compilation

Nous avons déjà compris le concept de compilation auparavant, parlons de la plateforme : le même code C++ sera compilé sous Windows Il devient un fichier .obj, tandis que sous Linux, il génère un fichier .o. Les deux ne peuvent pas être utilisés universellement. En effet, en plus du code, un fichier exécutable nécessite également des API du système d'exploitation, de la mémoire, des threads, des processus et d'autres ressources système, et les implémentations des différents systèmes d'exploitation sont également différentes. Par exemple, le multiplexage d'E/S (l'âme de l'événementiel) que nous connaissons, la solution d'implémentation sous Windows est la solution IOCP, et sous Linux, epoll. Par conséquent, les langages compilés doivent être compilés séparément ou même écrits séparément pour différentes plates-formes, et les formats des fichiers exécutables générés sont différents.

Multiplateforme

Java va encore plus loin en introduisant le bytecode pour réaliser un fonctionnement multiplateforme : quoi qu'il arrive Tous les fichiers .java compilés sur le système d'exploitation se trouvent des fichiers .class (il s'agit d'un fichier de bytecode, une forme intermédiaire de code cible). Ensuite, Java fournit différentes machines virtuelles Java pour différents systèmes afin d'interpréter et d'exécuter des fichiers de bytecode. L'interprétation et l'exécution ne génèrent pas de code objet, mais il sera éventuellement converti en instructions assembleur/binaires pour l'exécution informatique.

Si nous écrivons un nouveau système d'exploitation simple de manière totalement indépendante, peut-il exécuter Java ? Ce n’est évidemment pas possible, car il n’existe pas de JVM correspondante pour ce système. Par conséquent, le multiplateforme de Java et celui de tout autre langage ont des limites.

L'avantage de Java utilisant le semi-interprété et le semi-compilé est qu'il améliore considérablement l'efficacité du développement, mais réduit en conséquence l'efficacité de l'exécution du code. Après tout, les machines virtuelles subissent des pertes de performances.

Explication et exécution

JavaScript va encore plus loin. Il s'agit d'une exécution complètement interprétée, ou d'une compilation juste à temps. Il n’y aura pas de génération de code intermédiaire, ni de génération de code cible. Ce processus est généralement géré par l'environnement hôte (tel que le navigateur, Node.js).

Processus de compilation

现在我们确认了,即使是解释执行的语言,也是需要编译的。那么代码是如何编译的呢?我们来简单了解一下。

词法分析

词法分析会把语句分解成词法单元,即Token。

function square(n){
 return n*n;
}

这个函数会被词法分析器识别为function square(n){return,,n ,*n}并且给它们加上标注,代表这是一个变量还是一个操作。

语法分析

这个过程会把Token转化成抽象语法树(AST):

{
 type:'function',
    id:{
        type:'id'
        name:'square'
    },
    params:[
        {
            type:'id',
            name:'n'
        }
    ]
    ...
}

优化及代码生成

在这一步编译器会做一些优化工作,比如删除多余运算、删除未用赋值、合并部分变量等等操作,最后生成目标代码。

由于即时编译型语言的编译通常发生在运行前几微秒,所以编译器来不及做太多的优化工作。这也是相比编译型语言,早期JavaScript性能孱弱的原因之一。不过就现在而言,益于 V8 引擎(相比早期的JavaScript的引擎转换成字节码或解释执行,Node.js可以用 V8 提供的 JS2C 工具将 JavaScript 转译为 C++代码),JavaScript 和其他语言性能上的差距已经不足为道了。

链接及装载

目标代码基本不能独立运行。应用程序一般都会由多个部分(模块)组成 ,比如C++中一个简单的输出就要引入标准库 iostream

#include <iostream>
using namespace std;
int main(){    
    cout <p>编译器需要把多份目标代码(库)链接起来才能生成可执行文件。至此,我们简单的了解了编译过程。但实际上编译比我们所讲的要复杂得多,在此就不在展开了。</p>
<p><strong><span style="font-size: 18px;">什么是动态语言,动态类型?</span></strong></p>
<p>我们还知道,JavaScript是动态语言。那么什么是动态语言?</p>
<p>通常来说,这是指在运行时代码可以根据某些条件改变自身结构的语言。比如JavaScript在运行时新的函数、对象、甚至代码都可以被引进(eval);又比如Objective-C,它也可以在运行时修改对象,但它不能动态创建类,也没有 eval 方法。那Objective-C算是动态语言吗?所以我认为,动态语言是个程度的问题,我们不必在这个概念上太过纠结,可以更多的关注其应用。APP中常用的热更新功能就是基于动态语言特性而得以实现的。</p>
<p>JavaScript又是一门动态类型的语言,动态类型又是什么?动态类型的定义倒是很明确:数据类型不是在编译阶段确定,而是在运行时确定。</p>
<p>那么 TypeScript 是什么类型的语言呢?它有静态类型检查,它是静态语言吗?实际上它只是 JavaScript 的一个方言。TypeScript 最终还是要转译为 JavaScript 才能执行(tsc),就如同我们使用babel 把 ES6 代码转译为 ES5 一样。这个过程严格上来说不是编译。</p>
<p>TypeScript 最大的优势就是静态类型检查和类型推断,这是 JavaScript 严重缺失的能力。但实际上如果我们忽略IDE 给的报错提示强行运行 TS 代码,也还是有几率能够成功跑起来的。</p>
<p><span style="font-size: 18px;"><strong>错误</strong></span></p>
<p>刚刚我们提到报错,不妨再扩展说一说错误。通常来说错误分为以下几种:</p>
<ul>
<li>编译时错误</li>
<li>链接时错误</li>
<li>运行时错误</li>
</ul>
<p>是不是和编译过程能够严格对应起来?</p>
<p><strong>编译时错误</strong></p>
<p>编译时错误分为:</p>
<ul>
<li>
<p>语法错误</p>
<pre class="brush:php;toolbar:false">var str ='s ;

这就是典型的语法错误,这种代码无法生成AST,在词法分析阶段就会报错。通常我们这么写代码,IDE 就会报错。这是IDE的优化工作,和词法分析相关。

  • 类型错误

    编译器会检查我们声明的变量和函数的类型,JavaScript中我们非常熟悉的Type Error:undefined is not object就是此类错误。

  • 链接时错误

    在链接阶段发生的异常。这种情况 JavaScript 中比较少见,在编译型语言中比较常见。

    运行时错误

    这是最难排查的错误了,举例来说:

    int pider(int a,int b){
        return a/b;
    }

    上面的代码在编辑编译、链接阶段都没问题,也能够正常的生成可执行文件。但是一旦如此使用pider(1,0)就会报错了,这就是典型的运行时错误。通常来说运行时错误都是程序不够健壮导致的。

    JavaScript中最常见的十个错误:

    下图是某错误处理平台收集统计的JavaScript Top10 错误,其中7个TypeError,1个 ReferenceError:

    Explication détaillée du fonctionnement de JavaScript

    De toute évidence, nous pouvons utiliser TypeScript pour traiter ces 8 problèmes en temps opportun dès les premières étapes du codage.

    Conclusion

    Nous comprenons maintenant comment fonctionne JavaScript. Mais comprendre cela peut-il nous aider à écrire un meilleur code ?

    La réponse est oui. Sans oublier que TypeScript peut nous aider à améliorer la vérification de type et l'inférence de type, la portée et celle de JavaScript sont également fortement liées au processus de compilation et les petits frameworks de programmes actuels peuvent prendre en charge un ensemble de code et plusieurs plates-formes ; après avoir lu cet article, vous avez enfin une compréhension générale des principes qui sous-tendent ces technologies.
    Bon hacking !

    Cet article provient de la rubrique tutoriel js, bienvenue pour apprendre !

    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!

    Déclaration:
    Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer