Nodeがわからない

更新:この記事は、私の本「Node.js Beyond The Basics」の一部になりました。
jscomplete.com/node-beyond-basicsで、このコンテンツの更新バージョンとNodeの詳細をお読みください。

今年のForward.jsカンファレンス(JavaScriptカン​​ファレンス)で、「ノードを知らない」というタイトルの講演を行いました。その講演では、Nodejsランタイムに関する一連の質問で聴衆に挑戦しましたが、ほとんどの技術的な聴衆はほとんどの質問に答えることができませんでした。

私はそれを実際に測定しませんでしたが、それは確かに部屋でそう感じました、そして、数人の勇敢な人々が話の後に私に近づいて、事実を告白しました。

これが私にその話をさせた問題です。 Nodeに正しい方法を教えているとは思わない! Nodejsに関するほとんどの教育コンテンツは、ランタイムではなくNodeパッケージに焦点を当てています。これらのパッケージのほとんどは、ノードランタイム自体(httpやストリームなど)でモジュールをラップします。問題が発生した場合、これらの問題はランタイム自体の内部にある可能性があり、Nodeランタイムがわからない場合は問題が発生します。

問題:Nodejsに関するほとんどの教育コンテンツは、ランタイムではなくNodeパッケージに焦点を当てています。

この記事の機能に関する質問と回答をいくつか選びました。それらは以下のヘッダーに示されています。最初に頭で答えてみてください!

ここで間違った答えや誤解を招く答えを見つけた場合は、お知らせください。

質問1:呼び出しスタックとは何ですか?また、V8の一部ですか?

Call Stackは間違いなくV8の一部です。これは、関数呼び出しを追跡するためにV8が使用するデータ構造です。関数を呼び出すたびに、V8はその関数への参照を呼び出しスタックに配置し、他の関数のネストされた呼び出しごとにそのようにします。これには、自分自身を再帰的に呼び出す関数も含まれます。

Pluralsightコースから取得したスクリーンショット— Advanced Node.js

関数のネストされた呼び出しが終了すると、V8は一度に1つの関数をポップし、代わりに戻り値を使用します。

Nodeでこれを理解することが重要なのはなぜですか? Nodeプロセスごとに1つのCall Stackしか取得できないためです。そのコールスタックをビジーにしておくと、ノードプロセス全体がビジーになります。心に留めておきます。

質問#2:イベントループとは何ですか? V8の一部ですか?

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

Pluralsightコースから取得したスクリーンショット— Advanced Node.js

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

イベントループは、外部イベントを処理し、コールバック呼び出しに変換するエンティティです。これは、イベントキューからイベントを選択し、コールバックをコールスタックにプッシュするループです。また、多相ループです。

イベントループを初めて聞いた場合、これらの定義はそれほど役に立ちません。イベントループは、はるかに大きな図の一部​​です。

Pluralsightコースから取得したスクリーンショット— Advanced Node.js

イベントループを理解するには、その全体像を理解する必要があります。 V8の役割を理解し、Node APIを理解し、V8によって実行されるために物事がどのようにキューに入れられるかを知る必要があります。

Node APIは、setTimeoutやfs.readFileなどの関数です。これらはJavaScript自体の一部ではありません。それらはNodeが提供する機能です。

イベントループはこの図の真ん中にあり(実際にはもっと複雑なバージョンです)、主催者のように機能します。 V8 Call Stackが空の場合、イベントループは次に実行するものを決定できます。

質問#3:呼び出しスタックとイベントループキューがすべて空の場合、ノードは何をしますか?

単に終了します。

Nodeプログラムを実行すると、Nodeは自動的にイベントループを開始し、そのイベントループがアイドル状態で他に何もすることがない場合、プロセスは終了します。

Nodeプロセスを実行し続けるには、イベントキューのどこかに何かを配置する必要があります。たとえば、タイマーまたはHTTPサーバーを起動すると、基本的にイベントループにこれらのイベントの実行とチェックを継続するように指示します。

質問#4:V8とLibuv以外に、Nodeには他にどのような外部依存関係がありますか?

以下は、ノードプロセスが使用できるすべての個別のライブラリです。

  • http-parser
  • c-アレス
  • OpenSSL
  • zlib

それらはすべてNodeの外部にあります。彼らは独自のソースコードを持っています。彼らは独自のライセンスを持っています。ノードはそれらを使用します。

プログラムがどこで実行されているかを知りたいので、それを覚えておく必要があります。データ圧縮で何かをしている場合、zlibライブラリスタックの奥深くで問題が発生する可能性があります。 zlibのバグと戦っているかもしれません。 Nodeのすべてを非難しないでください。

質問#5:V8なしでNodeプロセスを実行することは可能でしょうか?

これは難しい質問かもしれません。 Nodeプロセスを実行するにはVMが必要ですが、使用できるVMはV8だけではありません。チャクラを使用できます。

node-chakraプロジェクトの進行状況を追跡するには、このGithubリポジトリを確認してください。

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

module.exportsをいつでも使用して、モジュールのAPIをエクスポートできます。 1つの場合を除き、エクスポートを使用することもできます。

module.exports.g = ... // OK
exports.g = ... // OK
module.exports = ... // OK
exports = ... // OKではありません

どうして?

exportsはmodule.exportsへの単なる参照またはエイリアスです。エクスポートを変更すると、その参照が変更され、公式API(module.exports)は変更されなくなります。モジュールスコープでローカル変数を取得するだけです。

質問7:トップレベル変数がグローバルではないのはなぜですか?

最上位変数gを定義するmodule1がある場合:

// module1.js
var g = 42;

そして、module1を必要とするmodule2があり、変数gにアクセスしようとすると、gが定義されていません。

どうして?ブラウザで同じ操作を行うと、定義後に含まれるすべてのスクリプトの最上位定義変数にアクセスできます。

すべてのNodeファイルは、背後で独自のIIFE(Immediately Invoked Function Expression)を取得します。 Nodeファイルで宣言されたすべての変数は、そのIIFEにスコープされます。

関連質問:この1行だけの次のNodeファイルを実行すると、出力はどうなりますか。

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

いくつかの引数が表示されます!

どうして?

Nodeが実行したのは関数だからです。 Nodeはコードを関数でラップし、その関数は上記の5つの引数を明示的に定義します。

質問#8:オブジェクトのexport、require、およびmoduleはすべてのファイルですべてグローバルに利用可能ですが、それらはすべてのファイルで異なります。どうやって?

requireオブジェクトを使用する必要がある場合、それをグローバル変数であるかのように直接使用するだけです。ただし、2つの異なるファイルでrequireを調べると、2つの異なるオブジェクトが表示されます。どうやって?

同じ魔法のIIFEのため:

ご覧のとおり、magic IIFEはコードに次の5つの引数を渡します:exports、require、module、__ filename、および__dirname。

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

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

module2を必要とするmodule1と、さらにmodule1を必要とするmodule2がある場合、どうなりますか?エラー?

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

エラーは発生しません。ノードはそれを許可します。

したがって、module1にはmodule2が必要ですが、module2にはmodule1が必要であり、module1はまだ完了していないため、module1はmodule2の部分バージョンを取得するだけです。

あなたは警告されました。

質問#10:ファイルシステム* syncメソッド(readFileSyncなど)を使用してもよい場合

Nodeのすべてのfsメソッドには同期バージョンがあります。なぜ非同期メソッドの代わりに同期メソッドを使用するのですか?

同期方法を使用しても問題ない場合があります。たとえば、サーバーのロード中に初期化ステップで使用できます。多くの場合、初期化ステップ後に行うすべての操作は、そこから取得したデータに依存します。コールバックレベルを導入する代わりに、同期メソッドを使用するのが1回限りであれば、同期メソッドを使用することもできます。

ただし、HTTPサーバーの要求時コールバックなどのハンドラー内で同期メソッドを使用している場合、それは単に100%間違っています。そんなことしたらダメ。

これらのチャレンジ質問の一部またはすべてに回答できたことを願っています。

読んでくれてありがとう。