JavaScript は表面的には単純に見えるかもしれませんが、内部では多くのことが起こっています。今日は、実行コンテキスト、ホイスティング、プリミティブ データ型と非プリミティブ データ型、型変換などの重要な概念について説明します。より良い、バグのないコードを作成したい場合は、これらを理解することが非常に重要です。
グローバルな実行コンテキストと語彙環境
ブラウザで JavaScript ファイルを実行すると、コードはコール スタックで 1 行ずつ実行されます。ただし、コードが実行される前に、グローバル実行コンテキストが作成されます。このコンテキストは、this オブジェクトと window オブジェクトを設定します。 Node.js では、window に相当するのは global であり、この 2 つを比較すると、window === global が true を返すことがわかります。
関数を呼び出すたびに、新しい字句環境が作成されます。グローバル実行コンテキストは最初に作成され、その内部で定義されたすべての関数はその変数にアクセスできます。これが JavaScript のスコープ チェーンの仕組みです。関数内から外側 (グローバル) スコープの変数にアクセスできます。
ホイスト: 変数と関数
JavaScript にはホイスティングと呼ばれるメカニズムがあり、コンパイル中に変数や関数がスコープの先頭に「移動」されます。仕組みは次のとおりです:
変数: var で宣言された変数は部分的にホイストされます。つまり、初期化される前に参照できますが、初期化される行に到達するまで値は未定義になります。
関数: 関数宣言構文で宣言された関数は完全にホイストされます。つまり、コード内で関数を宣言する前でも関数を呼び出すことができます。
例:
console.log(a); // undefined var a = 5; console.log(b); // Error: b is not defined let b = 10; hoistedFunction(); // Works! function hoistedFunction() { console.log('This function is hoisted!'); } notHoistedFunction(); // Error: notHoistedFunction is not a function var notHoistedFunction = function() { console.log('This function is not hoisted!'); }
ご覧のとおり、let と const は var のようにホイストされず、関数式 (notHoistedFunction など) は実行時にのみ定義されます。
プリミティブ型と非プリミティブ型
JavaScript には、プリミティブと非プリミティブの 2 種類のデータがあります。
プリミティブ型には、文字列、数値、ブール値、未定義、null、シンボル、bigint が含まれます。これらは不変であり、値を変更することはできません。例:
let x = 'hello'; x[0] = 'H'; // This won’t change the string, it stays 'hello'
非プリミティブ型は、オブジェクト、配列、関数です。これらは変更可能であり、参照によって渡されるため、値が変更される可能性があります。例:
let obj1 = { name: 'John' }; let obj2 = obj1; // Both obj1 and obj2 now reference the same object obj2.name = 'Doe'; console.log(obj1.name); // Outputs: Doe
元のオブジェクトの変更を避けるために、Object.assign() またはスプレッド演算子 (...) を使用して浅いコピーを作成できます。ネストされたオブジェクトをコピーするディープ コピーの場合は、JSON.parse() と JSON.stringify() を使用します。
サンプル コード スニペット: 浅いコピーと深いコピー
// Shallow copy example let obj1 = { name: 'John', details: { age: 30 } }; let obj2 = { ...obj1 }; // Shallow copy obj2.details.age = 40; console.log(obj1.details.age); // Output: 40 (Shallow copy affects the original) // Deep copy example let obj3 = JSON.parse(JSON.stringify(obj1)); // Deep copy obj3.details.age = 50; console.log(obj1.details.age); // Output: 40 (Deep copy doesn’t affect the original)
型の変換と比較
JavaScript は動的に型指定される言語です。つまり、変数の型を明示的に指定する必要はありません。ただし、これにより、特に比較演算子を使用する場合、予期しない動作が発生することがあります。
型の強制を避けるために、二重等号 (==) よりも常に三重等号 (===) を使用することを優先します。例:
console.log(0 == '0'); // true (type coercion happens) console.log(0 === '0'); // false (no type coercion)
NaN の比較などの特殊な場合には、NaN === NaN が false を返すため、Object.is() を使用します。
console.log(NaN === NaN); // false console.log(Object.is(NaN, NaN)); // true
JavaScript のランタイムと Node.js
JavaScript はシングルスレッドの同期ランタイムで実行されます。つまり、一度に 1 つのタスクしか実行できません。制限があるように思えるかもしれませんが、JavaScript は Web API とコールバック キューを使用して非同期タスクを効率的に処理します。仕組みは次のとおりです:
JavaScript は非同期タスク (setTimeout や HTTP リクエストなど) を検出すると、そのタスクを Web API に送信します。
コール スタックは残りのコードの実行を続けます。
非同期タスクが完了すると、コールバック キューに追加され、コール スタックが空になったときに実行されます。
Node.js は、V8 エンジンと libuv によるノンブロッキング I/O システムを使用して、このランタイムをサーバー側に拡張します。 Node.js は、他の操作をブロックすることなく複数のリクエストを処理できるシングルスレッドのイベント ループのアイデアを導入しました。
JavaScript が実行コンテキスト、ホイスティング、型変換、非同期タスクをどのように処理するかを理解することで、よりクリーンで効率的なコードを作成できるようになります。 JavaScript の動的な性質により、TypeScript などのツールは、コードを本番環境に対応できる静的型チェックを提供することで、よくある落とし穴を回避するのに役立ちます。
以上がJavaScript の謎を解く: 実行コンテキスト、ホイスティング、型変換を理解するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。