ホームページ >ウェブフロントエンド >jsチュートリアル >JavaScript 呼び出しスタックの謎を解く: コードが実際にどのように実行されるか

JavaScript 呼び出しスタックの謎を解く: コードが実際にどのように実行されるか

WBOY
WBOYオリジナル
2024-08-21 06:07:361033ブラウズ

Demystifying the JavaScript Call Stack: How Your Code Really Runs

JavaScript 呼び出しスタック がどのように機能するかということは、すべてのフロントエンド開発者がそのキャリアの中で少なくとも一度は尋ねたことがあることであり、私の意見では、これは誰もが知っている質問ではありません。ほとんどの場所で答えが得られますが、その答えは必ずしも明確ではなく、理解しやすいものではありません。だからこそ、この記事でこのトピックを取り上げることにしました。

最初から始めましょう。 JavaScript エンジンはコードを 1 行ずつ同期的に実行します。関数が実行されるたびに、実行コンテキスト (メモリ内のスペース のみが存在するすべてのスコープ付きプロパティを保存します) が作成されます。その関数内) そして関数を呼び出しスタックに追加します.

JavaScript はスタックの最上位にある関数のコードのみを実行し、関数が終了してその値を返すと、エンジンは呼び出しスタックから関数を削除します そして次の作業を開始します。

コール スタックが空である場合、JavaScript エンジンは次のグローバル コンテキストでコードの実行を継続します、または同様に、JavaScript ファイルのルート であり、どの関数にも属しません。

行ごとにいくつかの例を見てみましょう:


const num1 = 2;
const num2 = 5;

function sum(a, b){
return a + b;
}

const result= sum(num1, num2);
console.log(result) // 7

これは、2 つの定数 (num1 と num2) を定義し、2 つの数値を合計して合計の結果を返す関数

sum を定義する非常に単純なコードです。最後に、定数 result が作成され、引数 num1num2 を指定して sum を実行した 結果 がそれに代入されます。次に、結果の値がコンソールに出力されます。

これまでの説明が単純すぎる、または複雑すぎて何も説明されていないと思われる場合は、

ご容赦ください。興味深い内容に近づいています。

JavaScript エンジンが何をしているのかを

一行ずつ見てみましょう。最初の行では、エンジンはラベル num1 を作成し、値 2メモリに保存します

2 行目はラベル
const num1 = 2;
num2

に対して同じことを行います。ラベル num2 を作成し、値 5 をメモリに保存します。

これが意味するのは、
const num2 = 5;
グローバル スコープ

内の誰かが num2 の値を要求するたびに、エンジンがラベルを変更して値 5 を設定するということです。代わりに 次の行に進みましょう。次の行は

関数

sum です。エンジンはどうなると思いますか?関数を実行しようとしているのか、それとも呼び出しスタックに追加しようとしているのでしょうか? いいえ! エンジンが行うことは、sum という名前の新しいラベルを保存し、括弧内のコードをメモリに保存することです。または、同じことは、関数定義を保存し、それをsumラベルに割り当てます。

これまでに実行したコードのメモリを
function sum(a, b){
return a + b;
}
視覚的に確認できる

場合は、次のようになります。

Label name Value in memory
num1 2
num2 5
sum function definition
次の行は
本当に

興味深い行です。 JavaScript エンジンが次の行に到達すると、ラベル result を作成しますが、この時点では、ラベル にどのような値を割り当てる必要があるかまだわかりません。 >値は関数を実行した結果です。そのため、まず関数を実行し、関数が行う必要があることをすべて実行して、戻り値から結果を取得します
この時点で、エンジンは

コールスタックに関数を追加
const result= sum(num1, num2);
し、また

メモリ内の新しいスペースである新しい実行コンテキストも作成します。 JavaScript は、グローバル コンテキストと衝突することなく、引数の値と関数内の他のすべてのプロパティを保存できます。

First of all, the engine creates in memory the labels a and b which are the names given to the parameters, and it assigns the value of the arguments 2 and 5 respectively.

If we could see the memory at this specific moment, we would see something like this:

Label name Value in memory
a 2
b 5
return 2 + 5 = 7

In this case, the function is really simple and only returns the value of the sum between a and b, so the engine substitutes the parameters with the values of the arguments and returns the value to the global execution context. Finally, the function is removed from the call stack, and only the global context remains.

Call stack
Global

At this point, the result of the function is assigned to the label result and we can print the value on console with the console log.

Let's take a look at how the global memory looks now:

Label name Value in memory
num1 2
num2 5
sum function definition
result 7

Did you noticed? the label result has a value of 7? and also sum still has the function definition inside.

Let's take a look at the next code:

const num1 = 2;
const num2 = 5;

function sum(a, b){
return a + b;
}

const result= sum(num1, num2);
console.log(result) // 7

function sumThreeNumbers = (x,y,z) => {
return sum(x, y) + z
}

const result2 = sumThreeNumbers(4,6,2)
console.log(result2) // 12

The main difference is that now we have a new sumThreeNumbers function and we are creating a new result2 constant and assigning the value of running the function sumThreeNumbers with the arguments, 4, 6 and 2.

Let's take a look at how the call stack works when we run nested functions.

If we jump to the line when we define the constant result2 the global memory would look something like this:

Label name Value in memory
num1 2
num2 5
sum function definition
result 7
sumThreeNumbers function definition

Just as on the previous example, the JavaScript engine doesn't know what value to assign to the label result2, to get the value, first needs to execute the function sumThreeNumbers with the arguments. The function is added to the call stack, and a new execution context is created. The execution context would look like this:

Label name Value in memory
x 4
y 6
z 2

So the first thing that JavaScript does is create the parameter labels and assign the value provided by the arguments.

Now let's take a look at our call stack

Call stack
sumThreeNumbers
Global

As you can see, the call stack only has the sumThreeNumbers item (apart from the global context that is always present).

To be able to get the result value, the function sum needs to be executed first, so the engine will add the function to the call stack and create a new execution context for the sum function.

Call stack
sum
sumThreeNumbers
Global

As the sum function is on top of the call stack, Javascript needs to run sum first to be able to continue running sumThreeNumbers.

This is how it's going to look the execution context for the function sum:

Label name Value in memory
a 4
b 6
return 4 + 6 = 10

ご存知のとおり、_10 _が返され、呼び出しスタックから削除されます

Call stack
sumThreeNumbers
Global

JavaScript は sumThreeNumbers の実行を継続し、実行コンテキストは次のようになります:

Label name Value in memory
x 4
y 6
z 2
return 10 + 2 = 12

12 が返され、呼び出しスタックから削除されます。

Call stack
Global

その後、値 12 がプロパティ result2 に割り当てられ、値 12 がコンソールに表示されます。

この投稿が、JavaScript コールスタックの仕組みを理解するのに役立つことを願っています。もしそうなら、コメントや「いいね!」を残してください。次の記事でお会いしましょう!

以上がJavaScript 呼び出しスタックの謎を解く: コードが実際にどのように実行されるかの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

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