ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScriptの仕組みを詳しく解説

JavaScriptの仕組みを詳しく解説

青灯夜游
青灯夜游転載
2019-11-26 15:24:472496ブラウズ

JavaScriptの仕組みを詳しく解説

#JavaScript とは何ですか?

JavaScript の定義を確認しましょう。JavaScript は解釈された動的言語です。

インタープリタ言語は、コンパイル言語に対して相対的に存在し、ソース コードがターゲット コードに直接コンパイルされるのではなく、中間コードに変換され、インタープリタがその中間コードを解釈して実行します。

主流のプログラミング言語には、コンパイル型 (C など)、インタープリター型 (JavaScript など)、セミインタープリターおよびセミコンパイル型 (Java など) があります。

[関連コースの推奨事項: JavaScript ビデオ チュートリアル ]

コードはどのように実行されますか?

まず、コードがどのように実行されるかを理解しましょう。

コードは CPU によって実行されることがわかっていますが、現在の CPU は if...else などのステートメントを直接実行できず、バイナリ命令のみを実行できます。しかし、バイナリ命令は人間にとって非常に不親切です。バイナリ命令 1000010010101001 が何を表しているのかを迅速かつ正確に判断するのは困難です。そこで科学者たちはアセンブリ言語を発明しました。

アセンブリ言語

アセンブリ言語は、実際にはバイナリ命令のニーモニックです。

10101010 がメモリ読み取り操作を表し、メモリ アドレスが 10101111、レジスタ アドレスが 11111010 であると仮定すると、操作は完了します。 101010101010111111111010 は、特定のメモリ アドレスの値を読み取ってレジスタにロードすることを意味します。アセンブリ言語ではこの操作方法は変更されません。これは、バイナリ命令のマッピングにすぎません。

LD:10101010 
id:10101111
R:11111010

この例では、このように、上記の命令は LD id R として表現でき、コードの可読性が大幅に向上します。

しかし、これは十分に親切ではなく、CPU は 3 つのアドレスの式しか実行できず、人間の思考や言語パターンからはかけ離れています。そこで偉大な科学者たちは高級言語を発明しました。

高級言語

"コードは、機械が読むためではなく、人が読むために書かれています。

高級言語が「高度な」と呼ばれる理由は、それが私たちの思考や読書の習慣により一致しているからです。 if…elseこの種のステートメントは、1010101010 よりもはるかに快適に見えます。ただし、コンピュータは高級言語を直接実行できないため、高級言語を実行する前にアセンブリ言語/機械語に変換する必要があります。このプロセスがコンパイルです。

JavaScript をコンパイルする必要がありますか?

JavaScript は間違いなく高級言語であるため、実行する前に必ずコンパイルする必要があります。しかし、なぜそれをインタプリタ型言語と呼ぶのでしょうか?コンパイル言語、半インタープリタ言語、半コンパイル言語との違いは何ですか?コンパイルから始めましょう。

コンパイル

コンパイルの概念についてはすでに理解しました。プラットフォームについて話しましょう。同じ C コードが Windows 上でコンパイルされます。これは .obj ファイルになりますが、Linux では .o ファイルが生成されます。この 2 つは汎用的に使用することはできません。これは、実行可能ファイルにはコードに加えて、オペレーティング システムの API、メモリ、スレッド、プロセスなどのシステム リソースも必要であり、オペレーティング システムによって実装も異なるためです。たとえば、私たちがよく知っている I/O 多重化 (イベント駆動の魂)、Windows での実装ソリューションは IOCP ソリューション、Linux では epoll です。したがって、コンパイルされた言語は、プラットフォームごとに個別にコンパイルするか、さらには個別に記述する必要があり、生成される実行可能ファイルの形式は異なります。

クロスプラットフォーム

Java は、バイトコードを導入することでさらに一歩進んで、クロスプラットフォーム操作を実現します。コンパイルされたすべての .java ファイルは関係ありません。オペレーティング システム上には .class ファイルがあります (これはバイトコード ファイルであり、ターゲット コードの中間形式です)。次に、Java は、バイトコード ファイルを解釈して実行するために、さまざまなシステムにさまざまな Java 仮想マシンを提供します。解釈と実行ではオブジェクト コードは生成されませんが、最終的にはコンピューター実行用のアセンブリ/バイナリ命令に変換されます。

単純なオペレーティング システムを完全に独立して作成した場合、Java を実行できるでしょうか?このシステムには対応する JVM がないため、明らかに不可能です。したがって、Java のクロスプラットフォームと他の言語のクロスプラットフォームには制限があります。

セミインタープリットおよびセミコンパイルを使用する Java の利点は、開発効率が大幅に向上しますが、それに応じてコードの実行効率が低下することです。結局のところ、仮想マシンのパフォーマンスが低下します。

#説明と実行

JavaScript はさらに一歩進んだものです。これは完全に解釈された実行、つまりジャストインタイムのコンパイルです。中間コードの生成やターゲット コードの生成はありません。このプロセスは通常、ホスト環境 (ブラウザー、Node.js など) によって処理されます。

コンパイルプロセス

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

词法分析

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

    JavaScriptの仕組みを詳しく解説

    #TypeScript を使用すると、コーディングの初期段階でこれら 8 つの問題にタイムリーに対処できることは明らかです。

    #結論

    これで、JavaScript がどのように動作するかを理解できました。しかし、これを理解すると、より良いコードを書くことができるでしょうか?

    答えは「はい」です。 TypeScript が型チェックと型推論の改善に役立つことは言うまでもなく、JavaScript のスコープとこれもコンパイル プロセスに強く関係しており、現在主流の小規模プログラム フレームワークは 1 つのコード セットと複数のプラットフォームをサポートできます。この記事を読むと、最終的に、これらのテクノロジの背後にある原理を一般的に理解できるようになります。

    ハッピーハッキング!

    この記事は

    js チュートリアル 列からのものです。ぜひ学習してください。

    以上がJavaScriptの仕組みを詳しく解説の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

    声明:
    この記事はsegmentfault.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。