答えられないNode.jsに関する10の質問

今年、Forward.js JavaScriptカン​​ファレンスで、「Nodeがわからない」というプレゼンテーションを行いました。 プレゼンテーション中に、私は聴衆にNodeについていくつか質問をしましたが、出席者の多くはそれらに答えることができませんでした。 しかし、技術専門家は私の報告に耳を傾けました。 計算はしませんでしたが、すべてがそのように見え、パフォーマンス後にこれを確認したリスナーの何人かがこれを確認しました。


そのプレゼンテーションをさせた問題は、私の意見では、ノードトレーニングシステムが正しく構築されていないことです。 ほとんどのトレーニング資料はNodeパッケージに焦点を当てていますが、プラットフォーム自体には焦点を当てていません。 多くの場合、これらのパッケージはNodeモジュール( httpstream )のラッパーとして機能します。 その結果、Nodeを知らず、ソースが特定のパッケージではなくプラットフォームである可能性のある問題に直面している人は、非常に不利な立場にあります。

その会議からいくつかの質問と回答を選択し、それらをこの記事に含めました。 質問自体は、記事のセクションの見出しに示されています。 質問を読み込もうとして、これ以上読みませんが、最初に精神的に答えてください。 あなたが私の答えに間違いを見つけた場合- 私に知らせてください。

質問番号1。 コールスタックとは何ですか?また、V8エンジンの一部ですか?


Call Stackは間違いなくV8の一部です。 これは、V8が関数呼び出しを追跡するために使用するデータ構造です。 関数を呼び出すたびに、V8はこの関数へのリンクを呼び出しスタックに配置し、この関数から他の関数​​が呼び出されると、V8はそれらへの参照を使用して同じことを続けます。 さらに、自分自身を再帰的に呼び出す関数はスタックに入ります。


呼び出し履歴 Pluralsight Advanced Node.jsコースのスクリーンショット

ネストされた関数呼び出しで関数の実行が完了すると、V8は呼び出しスタックの最上部から関数へのリンクを抽出し、プログラムロジックが必要とする場所に返された値を置き換えます。

Nodeで作業するときに、これを理解することが重要なのはなぜですか? 実際には、ノードプロセスごとに1つの呼び出しスタックしかないということです。 スタックがいっぱいの場合、プロセスは何らかの作業でロードされます。 これは覚えておく価値があります。

質問番号2。 イベントループとは何ですか? V8エンジンの一部ですか?


次の図のイベントループはどこにあると思いますか?


環境V8。 Pluralsight Advanced Node.jsコースのスクリーンショット

イベントループはlibuvライブラリに実装されています。 V8の一部ではありません。

イベントループは、外部イベントを処理し、コールバック呼び出しに変換するエンティティです。 これは、イベントキューからイベントを取得し、コールスタックにコールバックをプッシュする、かなり複雑なループです。

これが一連のイベントについて初めて耳にする場合、上記の推論は特にわかりにくいかもしれません。 イベントループは、はるかに大きな図の一部​​です。


イベントのサイクル。 Pluralsight Advanced Node.jsコースのスクリーンショット

イベントループの本質を理解するために、それがどの環境で動作するかを知ることは有用です。 V8の役割、Node API、およびイベントキューの仕組み、それに関連付けられたコードがV8で実行される方法について理解する必要があります。

Node APIはsetTimeoutfs.readFileようなsetTimeout fs.readFile 。 JavaScriptの一部ではありません。 これらはNodeからアクセスできる機能にすぎません。

イベントのサイクルは、これらすべての中心にあり(もちろん、実際、これはすべてより複雑です)、主催者の役割を果たしています。 V8呼び出しスタックが空の場合、イベントループが次に何をするかを決定する場合があります。

質問番号3。 コールスタックとイベントループキューが空の場合、ノードは何をしますか?


答えは簡単です。ノードはシャットダウンするだけです。

アプリケーションを起動すると、Nodeは自動的にイベントループを開始し、イベントループがアイドル状態の場合、何もすることがない場合、プロセスは終了します。

Nodeプロセスが終了しないようにするには、イベントキューに何かを入れる必要があります。 たとえば、タイマーまたはHTTPサーバーを起動すると、これらのイベントの動作と監視を継続する必要があることをイベントループに通知します。

質問番号4。 V8エンジンとlibuvライブラリ以外に、Nodeには他にどのような外部依存関係がありますか?


Nodeプロセスで使用できるスタンドアロンライブラリを次に示します。


Nodeに関連して、それらはすべて外部です。 彼らには独自のソースコードがあり、その配布は個別のライセンスによって規制されています。 Nodeはそれらを使用するだけです。

これは、プログラムコードが実行される場所を正確に知るために覚えておく価値があります。 たとえば、データ圧縮に関与している場合、 zlibスタックの腸で発生した問題に遭遇する可能性があります。 おそらく、その理由はライブラリーのエラーであるため、Nodeのすべての非難を非難すべきではありません。

質問番号5。 V8なしでNodeプロセスを開始できますか?


これは難しい質問です。 Nodeプロセスを開始するには、JSエンジンが必要ですが、利用できるエンジンはV8だけではありません。 または、チャクラを使用できます。

node-chakraプロジェクトの詳細については、 この Githubリポジトリをご覧ください。

質問番号6。 module.exportsとexportsの違いは何ですか?


module.exportsコマンドを使用して、モジュールmodule.exportsをいつでもエクスポートできmodule.exports 。 1つの状況を除き、 exportsを使用できます。

 module.exports.g = ...  // Ok exports.g = ...         // Ok module.exports = ...    // Ok exports = ...           //   Ok 

なんで?

exports —コマンドは単なる参照であり、 module.exportsコンストラクトのエイリアスです。 exportsに何かを直接書き込もうとすると、結果としてそこに格納されているリンクを変更します。その後のexports呼び出し中に、この変数が公式APIで参照するもの(およびmodule.exports )を使用できなくなります。 。 exportsに何かを書いたら、このキーワードをモジュールのスコープ内にあるローカル変数に変えます。

質問番号7。 モジュールでトップレベル変数がグローバルではないのはなぜですか?


最上位変数gを定義するmodule1あるとします。

 // module1.js var g = 42; 

次に、 module1接続されている別のモジュールmodule1あり、変数gにアクセスしようとしますが、応答でエラーメッセージg is not definedれてg is not defined

なんで? 結局、ブラウザで同じことを行うと、スクリプトを接続した後、それらのグローバル変数にアクセスできます。

各Nodeファイルは、すぐに呼び出される独自の関数式(IIFE、Immediate Invoked Function Expression)でラップされます。 Nodeファイルで宣言された変数はすべてこのIIFE内にあり、外部からは見えません。

検討中の質問に関連する質問は次のとおりです。コードが1行しかない次のNodeファイルの起動後に表示されるもの:

 // script.js console.log(arguments); 

明らかに、いくつかの引数はコンソールに分類されます!


引数出力

なんで? 実際、このNodeファイルは関数として実行されます。 Nodeはコードを関数でラップし、この関数には5つの引数があります。これらは図に示されています。

質問番号8。 export、require、およびmoduleオブジェクトは各ファイルでグローバルに使用できますが、各ファイルには独自のインスタンスがあります。 これはどのように可能ですか?


requireオブジェクトが必要な場合、グローバル変数であるかのように単純に直接呼び出します。 ただし、2つの異なるファイルでrequireを調べるrequire 、2つの異なるオブジェクトがあることがわかります。 これはなぜですか?

IIFEについては、すでに私たちによく知られています。


Nodeの機能の研究

ご覧のとおり、IIFEはコードにexportsrequiremodule__filename 、および__dirname 5つの引数を__dirnameます。

これらの5つの変数は、Nodeで使用するとグローバルに見えますが、実際には通常の関数引数です。

質問番号9。 Nodeのモジュールの循環依存関係とは何ですか?


module1に依存するmodule2があり、 module2に依存するmodule1 、どうなりますか? エラーメッセージは表示されますか?

 // module1 require('./module2'); // module2 require('./module1'); 

エラーメッセージは表示されません。 ノードでも同様のことが可能です。

したがって、 module1接続されmodule2が、 module2接続され、 module1はまだ完全に準備が整っていないため、 module1module1の不完全なバージョンを受け取るだけmodule2 。 今、あなたはそれについて知っています。

質問番号10。 同期メソッドを使用してファイルシステムを操作することが許可されるのはいつですか(readFileSyncなど)?


Nodeのfsオブジェクトの各非同期メソッドには、同期バージョンがあります。 非同期メソッドの代わりに同期メソッドを使用する理由

同期メソッドには何も問題がない場合があります。 たとえば、サーバーの起動時の初期化段階で役立ちます。 多くの場合、初期化後に行われるすべてが初期化段階でロードされたデータに依存する場合、そのように使用されます。 データの1回限りのロードが実行される同様の状況でコールバックベースのコードを構築する代わりに、同期メソッドはまったく受け入れられます。

ただし、要求を処理するHTTPサーバーのコールバックなど、イベントハンドラー内で同期メソッドを使用する場合、これはオプションなしで完全に間違っています。 そうすることは強く推奨されません。

まとめ


これらすべての質問、または少なくともそれらのいくつかに答えていただければ幸いです。

親愛なる読者! この記事の著者ではなくJSの会議に参加していた場合、Node.jsのどの質問を聴衆に尋ねますか?

Source: https://habr.com/ru/post/J341646/


All Articles