JS実行の詳細を深く理解する

Jan 24, 2017 am 11:15 AM
js

JavaScript の定義から実行まで、JS エンジンは実装層で多くの初期化作業を実行します。そのため、JS エンジンの動作メカニズムを学ぶ前に、実行環境スタック、グローバル オブジェクト、実行といったいくつかの関連概念を導入する必要があります。環境、変数オブジェクト、アクティビティ オブジェクト、スコープ、スコープ チェーンなど、これらの概念は JS エンジンのコア コンポーネントです。この記事の目的は、各概念を個別に説明することではなく、簡単なデモを使用して分析を行い、JS エンジンの定義から実行までの詳細と、その中でこれらの概念が果たす役割を包括的に説明することです。

var x = 1;  //定义一个全局变量 xfunction A(y){   var x = 2;  //定义一个局部变量 x   function B(z){ //定义一个内部函数 B
       console.log(x+y+z);
   }   return B; //返回函数B的引用}var C = A(1); //执行A,返回BC(1); //执行函数B
ログイン後にコピー

このデモはクロージャであり、実行結果は 4 です。以下では、グローバル初期化実行関数 A実行関数 Bの 3 つの段階で JS エンジンの動作メカニズムを分析します。

1. グローバル初期化

JS エンジンが実行可能コードを入力すると、次の 3 つの初期化タスクを完了する必要があります:

まず、グローバル オブジェクト (グローバル オブジェクト) のコピーを 1 つだけ作成します。このオブジェクトはグローバルに存在し、プロパティはどこからでもアクセスでき、アプリケーションの存続期間中ずっと存在します。グローバル オブジェクトが作成されると、Math、String、Date、document などの一般的に使用される JS オブジェクトがその属性として使用されます。このグローバル オブジェクトには名前で直接アクセスできないため、別の属性 window があり、window 自体がポイントされているため、window を通じてグローバル オブジェクトにアクセスできます。疑似コードでシミュレートされたグローバル オブジェクトの一般的な構造は次のとおりです。

//创建一个全局对象var globalObject = { 
    Math:{},
    String:{},
    Date:{},
    document:{}, //DOM操作    ...
    window:this //让window属性指向了自身}
ログイン後にコピー

次に、JS エンジンは実行環境スタック (実行コンテキスト スタック) を構築する必要があります。同時に、グローバルも作成する必要があります。実行環境 (実行コンテキスト) EC を作成し、このグローバル実行環境 EC を実行環境スタックにプッシュします。実行環境スタックの機能は、プログラムが正しい順序で実行できるようにすることです。 JavaScript では、関数ごとに独自の実行環境があり、関数が実行されると、その関数の実行環境が実行環境スタックの先頭にプッシュされ、実行権限が取得されます。関数の実行が完了すると、その実行環境はスタックの最上位から削除され、実行権限は前の実行環境に戻ります。疑似コードを使用して、実行環境スタックと EC の間の関係をシミュレートします。

var ECStack = []; //定义一个执行环境栈,类似于数组var EC = {};   //创建一个执行空间,//ECMA-262规范并没有对EC的数据结构做明确的定义,你可以理解为在内存中分配的一块空间ECStack.push(EC); //进入函数,压入执行环境ECStack.pop(EC);  //函数返回后,删除执行环境
ログイン後にコピー

最後に、JS エンジンは EC に関連付けられたグローバル変数オブジェクト (Varibale オブジェクト) VO も作成し、VO をグローバル オブジェクトにポイントします。 VO には、グローバル オブジェクトの元のプロパティだけでなく、グローバルに定義された変数 x と関数 A も含まれます。同時に、関数 A を定義するときに、内部属性スコープが A に追加され、スコープは VO を指します。各関数が定義されると、それに関連付けられたスコープ属性が作成され、スコープは常に関数が定義されている環境を指します。このときの ECStack の構造は以下の通りです:

ECStack = [   //执行环境栈
    EC(G) = {   //全局执行环境
        VO(G):{ //定义全局变量对象
            ... //包含全局对象原有的属性
            x = 1; //定义变量x
            A = function(){...}; //定义函数A
            A[[scope]] = this; //定义A的scope,并赋值为VO本身        }
    }
];
ログイン後にコピー

2. 関数 A を実行します

実行が A(1) に入ると、JS エンジンは次の作業を完了する必要があります:

まず、JS エンジンは関数 A EC の実行環境を作成すると、EC が実行環境スタックの最上位にプッシュされ、実行権限を取得します。このとき、実行環境スタックには、グローバル実行環境と関数 A の実行環境の 2 つの実行環境が存在します。スタックの最上位に A の実行環境、最下位にグローバル実行環境があります。スタックの。次に、関数 A のスコープ チェーン (Scope Chain) を作成します。JavaScript では、実行環境ごとに識別子解決用のスコープ チェーンがあり、実行環境の作成時にそのスコープ チェーンに含まれるオブジェクトが初期化されます。現在実行中の関数の。

次に、JS エンジンは現在の関数の Activation オブジェクト (Activation Object) AO を作成します。ここでのアクティビティ オブジェクトは変数オブジェクトの役割を果たしますが、その名前は関数内で異なります (変数オブジェクトと考えることができます)。一般的な概念として、アクティブなオブジェクトはそのブランチです)、AO には関数の仮パラメータ、引数オブジェクト、このオブジェクト、およびローカル変数と内部関数の定義が含まれており、AO が先頭にプッシュされます。スコープチェーンの。関数 B を定義するときに、JS エンジンはスコープ属性も B に追加し、そのスコープが関数 B が定義されている環境を指すことに注意してください。関数 B が定義されている環境は、A のアクティブ オブジェクト AO です。 . AO はリンク リストのフロントエンドに位置します。リンク リストはエンドツーエンドで接続されているため、関数 B のスコープは A のスコープ チェーン全体を指します。 この時点での ECStack 構造を見てみましょう:

ECStack = [   //执行环境栈
    EC(A) = {   //A的执行环境
        [scope]:VO(G), //VO是全局变量对象
        AO(A) : { //创建函数A的活动对象
            y:1,
            x:2,  //定义局部变量x
            B:function(){...}, //定义函数B
            B[[scope]] = this; //this指代AO本身,而AO位于scopeChain的顶端,因此B[[scope]]指向整个作用域链
            arguments:[],//平时我们在函数中访问的arguments就是AO中的arguments            this:window  //函数中的this指向调用者window对象        },
        scopeChain:<AO(A),A[[scope]]>  //链表初始化为A[[scope]],然后再把AO加入该作用域链的顶端,此时A的作用域链:AO(A)->VO(G)    },
    EC(G) = {   //全局执行环境
        VO(G):{ //创建全局变量对象
            ... //包含全局对象原有的属性
            x = 1; //定义变量x
            A = function(){...}; //定义函数A
            A[[scope]] = this; //定义A的scope,A[[scope]] == VO(G)        }
    }
];
ログイン後にコピー

三、 执行函数B

函数A被执行以后,返回了B的引用,并赋值给了变量C,执行 C(1) 就相当于执行B(1),JS引擎需要完成以下工作:

首先,还和上面一样,创建函数B的执行环境EC,然后EC推入执行环境栈的顶部并获取执行权。 此时执行环境栈中有两个执行环境,分别是全局执行环境和函数B的执行环境,B的执行环境在栈顶,全局执行环境在栈的底部。(注意:当函数A返回后,A的执行环境就会从栈中被删除,只留下全局执行环境)然后,创建函数B的作用域链,并初始化为函数B的scope所包含的对象,即包含了A的作用域链。最后,创建函数B的活动对象AO,并将B的形参z, arguments对象 和 this对象作为AO的属性。此时ECStack将会变成这样:ECStack = [ //执行环境栈

    EC(B) = {   //创建B的执行环境,并处于作用域链的顶端
        [scope]:AO(A), //指向函数A的作用域链,AO(A)->VO(G)        var AO(B) = { //创建函数B的活动对象
            z:1,
            arguments:[],            this:window
        }
        scopeChain:<AO(B),B[[scope]]>  //链表初始化为B[[scope]],再将AO(B)加入链表表头,此时B的作用域链:AO(B)->AO(A)-VO(G)    },
    EC(A), //A的执行环境已经从栈顶被删除,
    EC(G) = {   //全局执行环境
        VO:{ //定义全局变量对象
            ... //包含全局对象原有的属性
            x = 1; //定义变量x
            A = function(){...}; //定义函数A
            A[[scope]] = this; //定义A的scope,A[[scope]] == VO(G)        }
    }
];
ログイン後にコピー

当函数B执行“x+y+z”时,需要对x、y、z 三个标识符进行一一解析,解析过程遵守变量查找规则:先查找自己的活动对象中是否存在该属性,如果存在,则停止查找并返回;如果不存在,继续沿着其作用域链从顶端依次查找,直到找到为止,如果整个作用域链上都未找到该变量,则返回“undefined”。从上面的分析可以看出函数B的作用域链是这样的:

AO(B)->AO(A)->VO(G)
ログイン後にコピー

因此,变量x会在AO(A)中被找到,而不会查找VO(G)中的x,变量y也会在AO(A)中被找到,变量z 在自身的AO(B)中就找到了。所以执行结果:2+1+1=4.

简单的总结语

了解了JS引擎的工作机制之后,我们不能只停留在理解概念的层面,而要将其作为基础工具,用以优化和改善我们在实际工作中的代码,提高执行效率,产生实际价值才是我们的真正目的。就拿变量查找机制来说,如果你的代码嵌套很深,每引用一次全局变量,JS引擎就要查找整个作用域链,比如处于作用域链的最底端window和document对象就存在这个问题,因此我们围绕这个问题可以做很多性能优化的工作,当然还有其他方面的优化,此处不再赘述,本文仅当作抛砖引玉吧!

 

 

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

JS と Baidu Maps を使用してマップ パン機能を実装する方法 JS と Baidu Maps を使用してマップ パン機能を実装する方法 Nov 21, 2023 am 10:00 AM

JS と Baidu Map を使用してマップ パン機能を実装する方法 Baidu Map は広く使用されている地図サービス プラットフォームで、Web 開発で地理情報、位置情報、その他の機能を表示するためによく使用されます。この記事では、JS と Baidu Map API を使用してマップ パン機能を実装する方法と、具体的なコード例を紹介します。 1. 準備 Baidu Map API を使用する前に、Baidu Map Open Platform (http://lbsyun.baidu.com/) で開発者アカウントを申請し、アプリケーションを作成する必要があります。作成完了

推奨: 優れた JS オープンソースの顔検出および認識プロジェクト 推奨: 優れた JS オープンソースの顔検出および認識プロジェクト Apr 03, 2024 am 11:55 AM

顔の検出および認識テクノロジーは、すでに比較的成熟しており、広く使用されているテクノロジーです。現在、最も広く使用されているインターネット アプリケーション言語は JS ですが、Web フロントエンドでの顔検出と認識の実装には、バックエンドの顔認識と比較して利点と欠点があります。利点としては、ネットワーク インタラクションの削減とリアルタイム認識により、ユーザーの待ち時間が大幅に短縮され、ユーザー エクスペリエンスが向上することが挙げられます。欠点としては、モデル サイズによって制限されるため、精度も制限されることが挙げられます。 js を使用して Web 上に顔検出を実装するにはどうすればよいですか? Web 上で顔認識を実装するには、JavaScript、HTML、CSS、WebRTC など、関連するプログラミング言語とテクノロジに精通している必要があります。同時に、関連するコンピューター ビジョンと人工知能テクノロジーを習得する必要もあります。 Web 側の設計により、次の点に注意してください。

株価分析に必須のツール: PHP と JS を使用してローソク足チャートを描画する手順を学びます 株価分析に必須のツール: PHP と JS を使用してローソク足チャートを描画する手順を学びます Dec 17, 2023 pm 06:55 PM

株式分析に必須のツール: PHP および JS でローソク足チャートを描画する手順を学びます。特定のコード例が必要です。インターネットとテクノロジーの急速な発展に伴い、株式取引は多くの投資家にとって重要な方法の 1 つになりました。株価分析は投資家の意思決定の重要な部分であり、ローソク足チャートはテクニカル分析で広く使用されています。 PHP と JS を使用してローソク足チャートを描画する方法を学ぶと、投資家がより適切な意思決定を行うのに役立つ、より直感的な情報が得られます。ローソク足チャートとは、株価をローソク足の形で表示するテクニカルチャートです。株価を示しています

PHP と JS を使用して株価ローソク足チャートを作成する方法 PHP と JS を使用して株価ローソク足チャートを作成する方法 Dec 17, 2023 am 08:08 AM

PHP と JS を使用して株のローソク足チャートを作成する方法。株のローソク足チャートは、株式市場で一般的なテクニカル分析グラフィックです。始値、終値、最高値、株価などのデータを描画することで、投資家が株式をより直観的に理解するのに役立ちます。株価の最低価格、価格変動。この記事では、PHP と JS を使用して株価のローソク足チャートを作成する方法を、具体的なコード例とともに説明します。 1. 準備 開始する前に、次の環境を準備する必要があります。 1. PHP を実行するサーバー 2. HTML5 および Canvas をサポートするブラウザー 3

JSとBaidu Mapsを使ってマップヒートマップ機能を実装する方法 JSとBaidu Mapsを使ってマップヒートマップ機能を実装する方法 Nov 21, 2023 am 09:33 AM

JS と Baidu Maps を使用してマップ ヒート マップ機能を実装する方法 はじめに: インターネットとモバイル デバイスの急速な発展に伴い、マップは一般的なアプリケーション シナリオになりました。視覚的な表示方法として、ヒート マップはデータの分布をより直観的に理解するのに役立ちます。この記事では、JS と Baidu Map API を使用してマップ ヒート マップ機能を実装する方法と、具体的なコード例を紹介します。準備作業: 開始する前に、次の項目を準備する必要があります: Baidu 開発者アカウント、アプリケーションの作成、対応する AP の取得

JSと百度地図を使って地図クリックイベント処理機能を実装する方法 JSと百度地図を使って地図クリックイベント処理機能を実装する方法 Nov 21, 2023 am 11:11 AM

JS と Baidu Maps を使用してマップ クリック イベント処理を実装する方法の概要: Web 開発では、地理的位置と地理情報を表示するためにマップ関数を使用することが必要になることがよくあります。マップ上のクリック イベント処理は、マップ機能の一般的に使用される重要な部分です。この記事では、JS と Baidu Map API を使用して地図のクリック イベント処理機能を実装する方法と、具体的なコード例を紹介します。手順: Baidu Map の API ファイルをインポートします。まず、Baidu Map API のファイルを HTML ファイルにインポートします。これは、次のコードによって実現できます。

PHP および JS 開発のヒント: 株価ローソク足チャートの描画方法をマスターする PHP および JS 開発のヒント: 株価ローソク足チャートの描画方法をマスターする Dec 18, 2023 pm 03:39 PM

インターネット金融の急速な発展に伴い、株式投資を選択する人がますます増えています。株式取引では、ローソク足チャートは一般的に使用されるテクニカル分析手法であり、株価の変化傾向を示し、投資家がより正確な意思決定を行うのに役立ちます。この記事では、PHP と JS の開発スキルを紹介し、株価ローソク足チャートの描画方法を読者に理解してもらい、具体的なコード例を示します。 1. 株のローソク足チャートを理解する 株のローソク足チャートの描き方を紹介する前に、まずローソク足チャートとは何かを理解する必要があります。ローソク足チャートは日本人が開発した

JSとBaidu Mapsを使って地図ポリゴン描画機能を実装する方法 JSとBaidu Mapsを使って地図ポリゴン描画機能を実装する方法 Nov 21, 2023 am 10:53 AM

JS と Baidu Maps を使用して地図ポリゴン描画機能を実装する方法 現代の Web 開発において、地図アプリケーションは一般的な機能の 1 つになっています。地図上にポリゴンを描画すると、ユーザーが表示および分析できるように特定のエリアをマークするのに役立ちます。この記事では、JS と Baidu Map API を使用して地図ポリゴン描画機能を実装する方法と、具体的なコード例を紹介します。まず、Baidu Map API を導入する必要があります。次のコードを使用して、Baidu Map API の JavaScript を HTML ファイルにインポートできます。

See all articles