MDNによると:
式は、値に解決される任意の有効なコード単位です。
そのため、右辺値として使用できるものはすべて式です。
基準は、副作用が存在するかどうかではありません。式には間違いなく副作用があります。たとえばa=2
、値 (2) を持ち、変数に値を代入する式です。そのため、次のようなことができます。
let a;
let b = 1 + (a = 2); // a is now 2 and b is 3
コンテキストによっては、同じ (テキスト) コード ブロックが式とステートメントの両方と見なされる可能性があります。たとえば、テキスト スニペットfunction f(){}
は、次のコードの 1 行目の式と 2 行目のステートメントです。
let g = function f() {};
function f() {};
したがって、何かが式であるかステートメントであるかは、(一般的なケースでは) 文脈から外れたテキスト コードを見て判断することはできません。むしろ、それは構文ツリー内のノードのプロパティであり、コードが (精神的にまたは実際に) 解析された後にのみ決定できます。
また、おそらくもっと重要なこととして、関数内の関数ステートメント (別名関数宣言) は、関数が呼び出さf
れたときに作成される実行コンテキストの一部を形成します。f
ただし、関数式はその実行コンテキストの一部を形成しません。
よく引用される影響の 1 つは、関数宣言は「持ち上げられる」のに対し、関数式はそうではないということです。
関数ステートメントは実行コンテキストでスペースを占有しますが、関数式は占有しないため、深層再帰ではより微妙な効果が実験的に観察されます。たとえば、以下のコードは関数の無限再帰を使用していますf
。最初のケースの関数f
には関数式が含まれており、2 番目のケースでは同等の関数宣言が含まれています。
// Function Expression
{
let i = 0;
try {
function f () {
i++;
(function g() {})(); // this is an expression
f();
}
f();
} catch (err) {
console.log(`Function Expressions case: depth of ${i} reached. Error: ${err.name}`);
}
}
// Function Declaration
{
let i = 0;
try {
function f () {
i++;
function g() {}; // this is a statement
g();
f();
}
f();
} catch (err) {
console.log(`Functions Declarations case: depth of ${i} reached. Error: ${err.name}`);
}
}
私のマシンでは、一貫して以下を取得します(node.jsで):
Function Expressions case: depth of 17687 reached. Error: RangeError
Functions Declarations case: depth of 15476 reached. Error: RangeError
…これは、関数宣言が実行コンテキストを保持するために必要なスペースの量を増やし、スタックスペースを少し速く消費し、最大再帰の深さをわずかに減らすという事実と一致しています。