開発者として、私は「レキシカル環境」という用語によく遭遇しましたが、時間をかけて詳しく調べたことはありませんでした。そこで、「共有することは思いやりである ;)」という理由から、この投稿で自分の発見を詳しく掘り下げて文書化することにしました。この投稿を終えるまでに、字句環境とは何かをしっかりと理解し、メモリ内で何が起こるか、データ構造とは何か、呼び出しスタックがどのように機能するかについても探求できることを願っています。心配しないでください - シンプルかつ明確に説明します!
詳細に入る前に、簡単な概要から始めましょう。いくつかの概念が最初は複雑に見えても心配しないでください。理解しやすくするためにそれらを分解し、たとえを使用します。
字句環境は、コード内の特定の時点で変数と関数のスコープを追跡する JavaScript の特別なデータ構造です。
データ構造は、情報を効率的に使用できるようにコンピュータ内で情報を整理および保存する方法です。一般的な例には、配列、オブジェクト、リスト、ツリーなどがあります。詳細: データ構造のチュートリアル - GeeksforGeeks
「字句」という用語は、変数と関数のスコープとアクセス可能性が、プログラムの実行方法ではなく、コード内のどこに記述されているかによって決定されることを意味します。
字句環境の主な役割:
これは、3 つの異なる字句環境を含む簡単なコード例です。
var sheep = 1; // Global sheep function drinkWater() { let waterTemperature = "cold"; console.log("The sheep is drinking " + waterTemperature + " water."); } var sheepHouseInArea = true; // Indicates whether the sheep house is present if (sheepHouseInArea) { let lambs = 2; // Lambs inside the house console.log("There are " + sheep + " sheep in the total area and " + lambs + " lambs!"); } // This will result in an error because 'lambs' // is only accessible inside the if block! console.log("How many lambs are there? " + lambs);
このコード内の 3 つの語彙環境は、グローバル スコープ、ドリンクウォーター関数スコープ、および if ブロック スコープです。これらの概念を理解しやすくするために、羊に関する簡単な例えを使用してみましょう。
今週外を歩いていると、柵で囲まれたエリアの中に数匹の羊を見つけて、「おお、これは語彙環境のようだ!」と思いました。
説明しましょう: 柵で囲まれたエリアの中に羊がいると想像してください。羊は柵の中で草を食べるなどのことしかできません。さて、柵の中に子羊が泊まれる小さな羊小屋があると想像してください。家の中の子羊は外に出ることはできませんが、外の羊は中に入ることができます。
フェンスは、羊、子羊、家、草など、すべてが存在するエリア全体を表しています。この柵で囲まれたエリアをグローバル スコープと呼びます。この柵で囲まれたエリア内では、羊小屋は小さな独立したセクションであり、ブロック範囲を表します。最後に、羊が食べる草 (yumyum) は、地球規模の範囲内の機能、つまり羊がその空間内で実行できる特定の活動またはアクションのようなものです。
コード ブロックでは、グローバル スコープは赤いボックスで表され、ドリンクウォーター関数のスコープは青いボックスで、if ブロックのスコープは緑のボックスで表されます。これらは 3 つの語彙環境です。
羊 (varsheep = 1; で表される) は、フェンスで囲まれたエリアを自由に歩き回る、グローバル スコープ内の変数を象徴します。これは、 DrinkWater 関数と if ブロックの外側と内側の両方で使用できます。
ドリンクウォーター関数は、羊が柵で囲まれたエリア内で実行できるアクションを表します。 DrinkWater 関数はグローバル スコープのどこからでも呼び出すことができます。ただし、関数自体は定義時に新しい字句環境を作成します。この関数内では、変数 (let WaterTemperature = 'cold'; など) は関数内でのみアクセス可能です。
if ブロックは、新しい小さいスコープを作成します。羊小屋で表されるこのスコープには、2 匹の子羊がいます (子羊 = 2 とする)。このスコープ内では、console.log ステートメントによって、lambs 変数の値とグローバル Sheep 変数の値が記録されます。 lambs 変数はブロック スコープに固有ですが、sheep 変数は親環境 (グローバル スコープ) からフェッチされます。これは、JavaScript がスコープ チェーンを検索し、現在の環境で見つからない変数を解決できるようにする外部環境参照によって可能になります。
外部環境参照は、字句環境内の参照またはポインタです。これは親の字句環境を指し、JavaScript がスコープ チェーンを検索することで現在の環境で見つからない変数を解決できるようにします。
水を飲むことができるグローバル スコープで定義された羊の総数を記録するように、 DrinkWater() 関数を変更できますか?コメントセクションで答えを共有してください!
このコードには、グローバル スコープ、関数スコープ、ブロック スコープという 3 つの字句環境があることがわかります。複数の字句環境がある場合、それを複数の字句環境と呼びます。単一のコード部分に複数の字句環境が存在できることを理解することが重要です。新しいスコープ (関数やブロックなど) が作成されるたびに、新しい字句環境が生成されます。つまり、コードのさまざまな部分が独自の個別の環境を持つことができます。
字句環境がどのように機能するかを理解したところで、環境レコードの概念をさらに詳しく見ていきましょう。
グローバル スコープ、関数、ブロックのいずれの場合でも、字句環境が作成されるたびに、JavaScript はその環境レコードを自動的に生成します。
この環境レコードは、その特定のスコープ内でアクセス可能なすべての変数、関数、その他のバインディングを追跡するデータ構造です。基本的に、その環境内で定義されたすべてのものの内部ストレージとして機能し、コードの実行中に必要なときに正しいデータを利用できるようにします。
字句環境と環境レコードの主な違い:
字句環境は、JavaScript コードが実行される場所です。これは、コードが存在する「設定」または「コンテキスト」と考えてください。このコンテキストには変数と関数のスコープが含まれており、コード内のどの時点でもどれが使用可能またはアクセス可能であるかを決定します。たとえば、私たちのコードでは、lambs 変数は緑の枠線の環境 (ブロック スコープ) 内でのみアクセスできます。字句環境には外部環境参照 (すでに説明しました) も含まれており、親環境の変数にアクセスできます。
環境レコードは、実際の変数、関数宣言、およびその環境で使用されるその他の識別子を保持する字句環境内の特定の記憶領域です。字句環境はより広いコンテキストですが、環境レコードはコードのデータ (変数値や関数定義など) が保存される場所です。 JavaScript が変数または関数にアクセスする必要があるときは常に、現在の字句環境の環境レコードを調べます。
コード例を使用して、字句環境と環境レコードをもう一度説明してみましょう。
3 つの語彙環境があり、それぞれに独自の環境レコードがあります。
これらの宣言はコード全体からアクセスできます。グローバル環境は、この環境で定義されている関数 DrinkWater と、実行時に独自のブロック スコープの作成につながる if ステートメントも参照します。
関数スコープ (ドリンクウォーター、青いボックス)、語彙環境 2:
この環境の環境レコードには、ドリンクウォーター関数内で let を使用して宣言された変数 WaterTemperature が含まれています。この変数は関数内でのみアクセスできます。ただし、この関数は羊のようにグローバル環境の変数にアクセスすることもできます。
ブロックスコープ (ブロックの場合、緑色のボックス)、字句環境 3:
この環境内の環境レコードには、if ブロック内で let を使用して宣言された変数 lamb が含まれています。この変数は、この特定のブロック スコープ内でのみアクセスできます。このブロックは、sheep やsheepHouseInArea などの親環境から変数にアクセスすることもできます。
字句環境と環境レコードを深く掘り下げた後、JavaScript がコード実行中にメモリと変数アクセスをどのように管理するかを理解する準備が整いました。
コードが実行されると、JavaScript はコードの関数またはブロックごとに新しい字句環境を作成します。各環境には独自の環境レコードがあり、そのスコープ内で定義されたすべての変数と関数が保存されます。この設定により、これまで説明したように効率的なメモリ使用が保証されます。
Behind the scenes, the JavaScript engine handles these lexical environments in memory. The call stack is used for tracking function calls, while block scopes create new lexical environments linked to their outer environments. However, unlike functions, these block scopes aren't pushed onto the call stack.
The call stack is a fundamental concept in how Javascript executes code.
The call stack is a data structure that keeps track of function calls in a program. It works on a Last-In-First-Out (LIFO) principle. Here's how it works:
Key points about the call stack:
Now you know why its called stack overflow haha!
Here's a simple example to illustrate:
function greet(name) { console.log('Hello, ' + name); } function processUser(user) { greet(user); } processUser('Alice');
As each function completes, it's popped off the stack until we return to the global context.
In our sheep code example, can you identify if anything is placed on the call stack during execution? Share your thoughts in the comments section!
That's it for Part 1! I hope this post has helped you gain a solid understanding of how JavaScript handles lexical environments, environment records, and what happens behind the scenes in memory. I've learned a lot in the process, and I hope you have too. If you have any questions or feedback, I'd love to hear from you - let's learn and improve together!
I titled this post 'Part 1' because I plan to follow up with 'Part 2,' where I'll dive into three major concepts that are closely linked to lexical environments:
These concepts are super important because they directly impact how your JavaScript code behaves. Understanding them will help you write cleaner, more efficient code and avoid some common pitfalls.
Stay tuned!
--
Please also follow me on my Medium: https://medium.com/@ensing89
以上がJS — JavaScript の字句環境を理解する — 詳細 — パート 1の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。