https://jsprimer.net/basic/statement-expression/
上記リンク先でjavascriptの「文と式」について勉強をしています。
上記リンク先の記述より「式」とは「評価値を生成し、変数に代入できるもの」、「文」とは「if(){}の様に変数に代入できないもの」と学んだのですが
上記リンク内「function宣言(文)とfunction式」の所に
javascript
1// learn関数を宣言する関数宣言文 2function learn() { 3} 4// 関数式をread変数へ代入 5const read = function() { 6};
とありました。
上記コードで「function learn() {}」が「関数宣言文」、「function() {};」が「関数式」を表していると思うのですが、このコードだと関数宣言「文(function() {
};)」を「変数(read)に代入」していて最初の「文とは変数に代入できない」という定義に反するのではないかと思い悩んでおります
どなたかここらへんの違いを教えて頂けないのでしょうか?
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/06/06 07:57
回答4件
0
jsprimerの間違い
前質問で私がjsprimerを勧めた手前恐縮ですが、jsprimerの説明に一部間違いがある事を前置きしておきます。
function宣言(文)とfunction式
「関数と宣言」の章において、関数を定義する方法を学びました。 functionキーワードから文を開始する関数宣言と、変数へ関数式を代入する方法があります。
関数宣言(文)と関数式は、どちらもfunctionというキーワードを利用しています。
「関数宣言(function宣言)」が文(Statement)の一部とする説明ですが、関数宣言は宣言の一部であって、文の一部では有りません。
これはJavaScriptの根幹仕様「ECMAScript」を読むと分かります。
翻って、冒頭の文に戻りますが、
文と式
本格的に基本文法について学ぶ前に、JavaScriptというプログラミング言語がどのような要素からできているかを見ていきましょう。
これも厳密には間違いで「文」と「式」以外にも構成要素があります。
最新のECMAScript2019仕様でも、**文(Statement)と宣言(Declaration)**は明確に区別されています。
Syntax
Statement[Yield, Await, Return]: BlockStatement[?Yield, ?Await, ?Return] VariableStatement[?Yield, ?Await] EmptyStatement ExpressionStatement[?Yield, ?Await] IfStatement[?Yield, ?Await, ?Return] BreakableStatement[?Yield, ?Await, ?Return] ContinueStatement[?Yield, ?Await] BreakStatement[?Yield, ?Await] [+Return]ReturnStatement[?Yield, ?Await] WithStatement[?Yield, ?Await, ?Return] LabelledStatement[?Yield, ?Await, ?Return] ThrowStatement[?Yield, ?Await] TryStatement[?Yield, ?Await, ?Return] DebuggerStatement Declaration[Yield, Await]: HoistableDeclaration[?Yield, ?Await, ~Default] ClassDeclaration[?Yield, ?Await, ~Default]
この文と式の違いを見ると、関数宣言文にはセミコロンがなく、関数式にはセミコロンがあります。 このような、違いがなぜ生まれるのかは、ここまでの内容から説明できます。
関数宣言(文)で定義したlearn関数には、セミコロンがありません。 これは、ブロックで終わる文にはセミコロンが不要であるためです。
関数宣言(FunctionDeclaration)の {}
はブロック(Block)ではありません。
FunctionDeclaration[Yield, Await, Default]: function BindingIdentifier(FormalParameters) { FunctionBody } function (FormalParameters){ FunctionBody }
FunctionDeclaration の {}
がBlockであるならば、Syntaxは下記のようになっているべきでした。
上述に { FunctionBody }
とあるように、この {} は関数宣言独自の特別なSyntaxです。
FunctionDeclaration function BindingIdentifier(FormalParameters) Block
宣言(Declaration)と文(Statement)と式(Expression)
まず、大前提ですが、
- 宣言(Declaration)
- 文(Statement)
- 式(Expression)
これらは全て文法(Syntax)の話です。
今まで紹介した ECMAScript2019 上でも全てSyntaxの欄に定義されていました。
- ECMAScript 2019のSyntax上で Declaration とある場所に「宣言」を書くことが出来ます
- ECMAScript 2019のSyntax上で Statement とある場所に「文」を書くことが出来ます
- ECMAScript 2019のSyntax上で Expression とある場所に「式」を書くことが出来ます
そして、重要な違いは
- 宣言と文は単体で動作する
- 式は単体では動作せず、文または宣言の一部としてしか使用できない
という性質にあり、jsprimmer上では、それを
評価した結果を変数に代入できるものは式であるという理解で問題ありません。
JavaScript
1// (中略) 2 3// 式の評価値を変数に代入 4const total = 1 + 1;
と説明していますが、これは const 宣言のSyntaxを追いかけていくと、
LexicalDeclaration LetOrConstBindingList LetOrConst: let const
「LexicalDeclaration -> BindingList -> LexicalBinding -> Initializer -> AssignmentExpression」となり、「Expression」の一部である「AssignmentExpression」にたどり着く事を表しています。
式文(ExpressionStatement)
そうはいっても、式(Expression)を単体で動作させたい状況もあるので、式文(ExpressionStatement)という構文があります。
これは式(Expression)を一つ記述し、;
で終わる簡単なSyntaxです。
ただし、文(Statement)と宣言(Declaration)との整合性をとる為に、特別ルールが規定されています。
NOTE: An ExpressionStatement cannot start with a U+007B (LEFT CURLY BRACKET) because that might make it ambiguous with a Block.
An ExpressionStatement cannot start with the function or class keywords because that would make it ambiguous with a FunctionDeclaration, a GeneratorDeclaration, or a ClassDeclaration.
An ExpressionStatement cannot start with async function because that would make it ambiguous with an AsyncFunctionDeclaration or a AsyncGeneratorDeclaration.
An ExpressionStatement cannot start with the two token sequence let [ because that would make it ambiguous with a let LexicalDeclaration whose first LexicalBinding was an ArrayBindingPattern.
Google翻訳結果
「注: ExpressionStatementをU + 007B(LEFT CURLY BRACKET)で開始することはできません。
これは、Blockがあいまいになる可能性があるためです。
ExpressionStatementをfunctionまたはclassキーワードで始めることはできません。
これは、FunctionDeclaration、GeneratorDeclaration、またはClassDeclarationがあいまいになるためです。
AsyncFunctionDeclarationまたはAsyncGeneratorDeclarationがあいまいになるため、ExpressionStatementを非同期関数で始めることはできません。 ExpressionStatementは、2つのトークンシーケンスlet [で始めることはできません。
これは、最初のLexicalBindingがArrayBindingPatternであるlet LexicalDeclarationで曖昧になるためです。」
これがjsprimerで「functionキーワードから文を開始する関数宣言」と説明されている理由ですが、対比の為に「式文はfunctionキーワードで始める事はできない」とあると、分かりやすかったかもしれません。
まとめ
- 式は単体で動作せず、文または宣言の一部として動作する
- 関数宣言は
function
キーワードで始まらなければならない - 式文は
function
キーワードで始める事はできない
更に要約すると、次のようになります。
function
キーワードで始められるのは関数宣言のみ- 間数式を格納している文または宣言(※関数宣言を除く)は
function
キーワードで始める事は出来ない
[参考] function キーワードで始まらないコード
「名前のない即時関数」を書く場合を想定すると、function キーワードの違いが分かります。
下記コードはGoogle Chromeで実行した結果ですが、
JavaScript
1function(){} // SyntaxError: Function statements require a function name
functionキーワードで始められるのは関数宣言のみの為、JavaScriptエンジンはこのコードを関数宣言として扱おうとしますが、名前がない関数宣言が記述されている為、SyntaxError(文法エラー)を返しています。
(※前述の通り、関数宣言は文ではない為、エラーメッセージ内の "Function statements"
は間違いです)
()
で括って、functionキーワードで始まらない記述にすれば、エラーは発生しません。
JavaScript
1(function(){}); // エラーにならない
();
を末尾につけて、実行すれば、よく見る即時関数の形式になります。
JavaScript
1(function(){ 2 console.log('Hello, World!'); 3})(); 4 5(function(){ 6 console.log('Hello, JavaScript!'); 7}());
function
キーワードで始まっていないので、このコードは「関数宣言(FunctionDeclaration)」ではありませんfunction
キーワードで始まっていないので、このコードは「式文(ExpressionStatement)」です
Re: tanakashouzoux さん
投稿2020/06/06 11:01
編集2020/06/07 03:32総合スコア18189
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/06/06 11:44 編集
2020/06/07 22:29
0
MDNの関数式の項に解説はあります。
関数式と関数宣言の主な相違点は、**関数名です。関数式では、無名関数を生成するために、関数名を省略できます。関数式は、定義するとすぐに実行する IIFE (即時実行関数)として使用できます。**詳細については、関数の章を参照してください。
あと
このコードだと関数「式」を「変数に代入」していて最初の「式とは変数に代入できない」という定義に反するのではないかと思い悩んでおります
よく考えようとされていて感心するところではありますが、今回は「関数」「式」と2つの名詞を組み合わせたものではなく**「関数式」という1つの固有名詞**として認識した方が良いと思います。
雑に説明すると「そういうもの」です。
投稿2020/06/06 07:51
編集2020/06/06 07:55総合スコア80875
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/06/06 08:11 編集
2020/06/06 08:11
2020/06/06 08:12
2020/06/06 08:12
2020/06/06 08:16
2020/06/06 08:16
2020/06/06 08:17
2020/06/06 08:18
2020/06/06 08:19
2020/06/06 08:20
2020/06/06 08:22
2020/06/06 08:28
2020/06/06 08:33
2020/06/06 08:36
2020/06/06 08:38
2020/06/06 08:57
2020/06/08 11:23
0
ベストアンサー
jsprimer上の間違い
「jsprimer上の間違い」と「質問の回答」が混在して分かりづらくなっていた為、回答を分割しました。
本回答では「質問の回答」を行い、「jsprimer上の間違い」は下記回答で行います。
ECMAScriptの文法
本質問はECMAScriptの文法に関わる問題であり、概要を下記リンク先にまとめました。
以降の説明は、この内容を理解している前提で進めます。
関数宣言と関数式
jsprimerよりコードを引用します。
JavaScript
1// learn関数を宣言する関数宣言文 2function learn() { 3}
このコードは**関数宣言(文ではありません)**で間違いありません。
前節のqiitaの記事で解説しています。
JavaScript
1// 関数式をread変数へ代入 2const read = function() { 3};
このコードは、**const宣言(Const Declaration)**です。
Syntax上の名前の「レキシカル宣言(LexicalDeclaration)」と呼んでも構いません。
Syntaxを追いかけていけば、AssignmentExpressionに辿りつく事が分かります。
AssignmentExpressionは「式」であり、原則として式の中に宣言を入れる事は出来ません。
つまり、const宣言で代入可能なものは式に限定される為、このコードで代入されている関数は「関数宣言」になりえず、「関数式」が確定します。
※そもそもですが、関数宣言は名前が必須なので、名前を省略している時点で関数宣言になりえません。
Re: tanakashouzoux さん
投稿2020/06/07 07:59
編集2020/06/07 08:09総合スコア18189
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/06/07 23:24
0
「文」とは「評価値を生成し、変数に代入できるもの」、「式」とは「if(){}の様に変数に代入できないもの」と学んだのですが
逆です。
#コメントを受けての追記
無名関数だと関数式、名前が付いている関数は関数宣言文と考えれば良いのでしょうか???
名前付きの関数式のサンプルです。
JavaScript
1// 階乗計算 2let foo = function bar(n) { return n<=1 ? 1 : n*bar(n-1); }; 3 4console.log(foo(3)); // これは6と表示 5console.log(bar(3)); // これはbarという関数が未定義というエラー
投稿2020/06/06 07:56
編集2020/06/06 09:15総合スコア85762
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/06/06 08:00
2020/06/06 08:02
2020/06/06 08:06
2020/06/06 08:19
2020/06/06 08:26
2020/06/06 08:35
2020/06/06 08:55
2020/06/06 09:11
2020/06/06 10:46
2020/06/06 10:53
2020/06/07 22:07
2020/06/08 02:36
2020/06/08 04:55
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。