Heim  >  Artikel  >  Web-Frontend  >  Ausführliche Erklärung der Funktionsweise von JavaScript

Ausführliche Erklärung der Funktionsweise von JavaScript

青灯夜游
青灯夜游nach vorne
2019-11-26 15:24:472452Durchsuche

Ausführliche Erklärung der Funktionsweise von JavaScript

Was ist JavaScript?

Bestätigen wir die Definition von JavaScript: JavaScript ist eine interpretierte dynamische Sprache.

Interpretierte Sprachen existieren relativ zu kompilierten Sprachen. Der Quellcode wird nicht direkt in Zielcode kompiliert, sondern in Zwischencode umgewandelt, der dann vom Interpreter interpretiert und ausgeführt wird.

Zu den gängigen Programmiersprachen gehören kompilierte (wie C++), interpretierte (wie JavaScript) sowie halbinterpretierte und halbkompilierte Sprachen (wie Java).

[Verwandte Kursempfehlungen: JavaScript-Video-Tutorial]

Wie läuft der Code ab?

Lassen Sie uns zunächst verstehen, wie der Code ausgeführt wird.

Wir wissen, dass der Code von der CPU ausgeführt wird und die aktuelle CPU Anweisungen wie if…else nicht direkt ausführen kann, sondern nur binäre Anweisungen. Aber binäre Anweisungen sind für Menschen wirklich unfreundlich: Es ist für uns schwierig, schnell und genau zu bestimmen, was eine binäre Anweisung 1000010010101001 darstellt? Also erfanden Wissenschaftler die Assemblersprache.

Assemblersprache

Assemblersprache ist eigentlich eine Eselsbrücke für binäre Anweisungen.

Angenommen, 10101010 stellt einen Speicherlesevorgang dar, die Speicheradresse ist 10101111 und die Registeradresse ist 11111010. Dann stellt der vollständige Vorgang 101010101010111111111010 das Lesen des Werts einer bestimmten Speicheradresse dar und das Laden in Register, und die Assemblersprache ändert diesen Betriebsmodus nicht, es handelt sich lediglich um eine Zuordnung binärer Anweisungen:

LD:10101010 
id:10101111
R:11111010

Auf diese Weise können die obigen Anweisungen als LD id R ausgedrückt werden, was sehr hilfreich ist verbessert die Lesbarkeit des Codes.

Aber das ist nicht benutzerfreundlich genug. Die CPU kann nur Ausdrücke mit drei Adressen ausführen, was weit von menschlichen Denk- und Sprachmustern entfernt ist. So erfanden große Wissenschaftler Hochsprachen.

Hochsprache

„Code wird geschrieben, damit Menschen ihn lesen können, nicht damit Maschinen ihn lesen können. Er kann nur vom Computer ausgeführt werden.“ es.“

Der Grund, warum Hochsprache „Hochsprache“ genannt wird, liegt darin, dass sie eher unseren Denk- und Lesegewohnheiten entspricht. if…else Diese Art von Satz sieht viel bequemer aus als 1010101010. Allerdings können Computer Hochsprachen nicht direkt ausführen, daher müssen sie Hochsprachen in Assemblersprache/Maschinenanweisungen umwandeln, bevor sie ausgeführt werden können. Dieser Prozess ist Kompilierung.

Muss JavaScript kompiliert werden?

JavaScript ist zweifellos eine Hochsprache und muss daher unbedingt kompiliert werden, bevor es ausgeführt werden kann. Aber warum nennen wir es eine interpretierte Sprache? Was ist der Unterschied zwischen kompilierten Sprachen und halbinterpretierten und halbkompilierten Sprachen? Beginnen wir mit der Zusammenstellung.

Kompilierung

Wir haben das Konzept der Kompilierung bereits zuvor verstanden. Lassen Sie uns über die Plattform sprechen: Derselbe C++-Code wird unter Windows kompiliert Es wird eine .obj-Datei, während es unter Linux eine .o-Datei generiert. Die beiden können nicht universell verwendet werden. Dies liegt daran, dass eine ausführbare Datei neben Code auch Betriebssystem-APIs, Speicher, Threads, Prozesse und andere Systemressourcen erfordert und verschiedene Betriebssysteme unterschiedliche Implementierungen haben. Beispielsweise ist das I/O-Multiplexing (die Seele des ereignisgesteuerten Verfahrens), mit dem wir vertraut sind, die Implementierungslösung unter Windows die IOCP-Lösung und unter Linux Epoll. Daher müssen kompilierte Sprachen für verschiedene Plattformen separat kompiliert oder sogar separat geschrieben werden, und die generierten ausführbaren Dateien haben unterschiedliche Formate.

Plattformübergreifend

Java geht noch einen Schritt weiter, indem es Bytecode einführt, um einen plattformübergreifenden Betrieb zu erreichen: egal was Alle .java-Dateien kompiliert werden Auf dem Betriebssystem befinden sich .class-Dateien (dies ist eine Bytecode-Datei, eine Zwischenform des Zielcodes). Dann stellt Java verschiedene virtuelle Java-Maschinen für verschiedene Systeme bereit, um Bytecode-Dateien zu interpretieren und auszuführen. Durch Interpretation und Ausführung wird kein Objektcode generiert, sondern dieser wird schließlich in Assembler-/Binäranweisungen zur Computerausführung umgewandelt.

Wenn wir völlig unabhängig ein neues einfaches Betriebssystem schreiben, kann es dann Java ausführen? Offensichtlich ist dies nicht möglich, da es für dieses System keine entsprechende JVM gibt. Daher weisen die plattformübergreifenden Funktionen von Java und jede andere Sprache Einschränkungen auf.

Der Vorteil der Verwendung von halbinterpretiertem und halbkompiliertem Java besteht darin, dass die Entwicklungseffizienz erheblich verbessert wird, die Effizienz der Codeausführung jedoch entsprechend verringert wird. Schließlich weisen virtuelle Maschinen Leistungsverluste auf.

Erklärung und Ausführung

JavaScript geht noch einen Schritt weiter. Es handelt sich um eine vollständig interpretierte Ausführung oder Just-in-Time-Kompilierung. Es erfolgt weder eine Zwischencodegenerierung noch eine Zielcodegenerierung. Dieser Prozess wird normalerweise von der Hostumgebung (z. B. Browser, Node.js) durchgeführt.

Kompilierungsprozess

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

词法分析

词法分析会把语句分解成词法单元,即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:

    Ausführliche Erklärung der Funktionsweise von JavaScript

    Natürlich können wir TypeScript verwenden, um diese 8 Probleme in den frühen Phasen der Codierung rechtzeitig zu lösen.

    Fazit

    Jetzt verstehen wir, wie JavaScript funktioniert. Aber kann uns das Verständnis dabei helfen, besseren Code zu schreiben?

    Die Antwort ist ja. Ganz zu schweigen davon, dass TypeScript uns dabei helfen kann, die Typprüfung und Typinferenz zu verbessern. Der Umfang und dies von JavaScript hängen auch stark mit dem Kompilierungsprozess zusammen, und die aktuellen Mainstream-Frameworks für kleine Programme können einen Codesatz und mehrere Plattformen unterstützen Nachdem Sie diesen Artikel gelesen haben, haben Sie schließlich ein allgemeines Verständnis der Prinzipien hinter diesen Technologien.
    Viel Spaß beim Hacken!

    Dieser Artikel stammt aus der Spalte JS-Tutorial, willkommen zum Lernen!

    Das obige ist der detaillierte Inhalt vonAusführliche Erklärung der Funktionsweise von JavaScript. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

    Stellungnahme:
    Dieser Artikel ist reproduziert unter:segmentfault.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen