質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

4回答

2664閲覧

javascriptにおける関数文と関数式の違いとは何なのでしょうか?

tanakashouzoux

総合スコア52

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

1クリップ

投稿2020/06/06 07:47

編集2020/06/06 08:07

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ページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Zuishin

2020/06/06 07:53

式が変数に代入できないとどこに書いてありますか?
tanakashouzoux

2020/06/06 07:57

すいません! 式と文が逆でした???? 質問を訂正させて頂きます!
guest

回答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
think49

総合スコア18189

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

think49

2020/06/06 11:44 編集

一部、コメントで「名前付き関数式」の優位性、「関数式と関数宣言の機能的な違い」の議論が白熱していますが、本題は「jsprimerの説明文の読み方」と解釈したので、そこには触れませんでした。 過去に回答/質問で言及しているので、気になる方は下記を参考にして頂ければと思います。 https://teratail.com/questions/24115 https://teratail.com/questions/27900#reply-43667
tanakashouzoux

2020/06/07 22:29

think49さん わざわざご丁寧に2つも回答頂きありがとうございますm(__)m 少し拝読するのに時間がかかっております(私の理解力の無さの為)ので、また改めてコメントさせて頂きたいと思いますm(__)m
guest

0

MDNの関数式の項に解説はあります。

関数式と関数宣言の主な相違点は、**関数名です。関数式では、無名関数を生成するために、関数名を省略できます。関数式は、定義するとすぐに実行する IIFE (即時実行関数)として使用できます。**詳細については、関数の章を参照してください。

あと

このコードだと関数「式」を「変数に代入」していて最初の「式とは変数に代入できない」という定義に反するのではないかと思い悩んでおります

よく考えようとされていて感心するところではありますが、今回は「関数」「式」と2つの名詞を組み合わせたものではなく**「関数式」という1つの固有名詞**として認識した方が良いと思います。
雑に説明すると「そういうもの」です。

投稿2020/06/06 07:51

編集2020/06/06 07:55
m.ts10806

総合スコア80875

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tanakashouzoux

2020/06/06 08:11 編集

わざわざご丁寧に回答頂きありがとうございますm(__)m 「関数式」という1つの固有名詞として認識すると良いのですね! ありがとうございます!!! 質問できる人もおらず自分の知識が正しいのか確認する事さえ出来なかったのでご回答頂けて本当にありがたいですm(__)m ちなみに「関数宣言文」も1つの固有名詞として考えた方がよろしいのでしょうか?
m.ts10806

2020/06/06 08:12

仕様が気になるなら日本語解説が充実しているMDNです。
m.ts10806

2020/06/06 08:12

>関数式という特別なものがあるのではなく、function 宣言は元々式なのではないでしょうか? そんな気がします。結構「式」って意味が広いですね。
maisumakun

2020/06/06 08:16

> 関数式という特別なものがあるのではなく、function 宣言は元々式なのではないでしょうか? 巻き上げの挙動が違います。 function宣言は同じスコープであれば、宣言より前の行でも呼べますが、関数式を変数に代入する場合は、代入が実行されてからでないと呼べません。
Zuishin

2020/06/06 08:16

文しかなかった function 宣言が式に拡張されたという歴史的経緯がありそうな予感がします。
m.ts10806

2020/06/06 08:17

>巻き上げの挙動が違います。 これは1つ勉強になりました。ありがとうございます。
tanakashouzoux

2020/06/06 08:18

Zuishinさん、m.ts10806さん、わざわざ何度もご指導ありがとうございますm(__)m 初心者なもので頭が少し混乱してきたのですが、上記サイトでは「function宣言(文)」と書かれていたのですがこれは本当は「function宣言(式)」ということなのでしょうか? それとも何か特殊な事情があるのでしょうか...??
Zuishin

2020/06/06 08:19

なるほど、巻き上げの挙動を確かめてみましたが、確かに違いますね。
tanakashouzoux

2020/06/06 08:20

maisumakunさん今回もご指導ありがとうございますm(__)m 前回御指導頂いた時に巻き上げの挙動の違いを教えて頂き、先日上記サイトで巻き上げを目にした時は「これが指導頂いた巻き上げか!知ってる!!!」と感動した所でした! 本当にありがとうございます! function宣言では巻き上げが行われ、関数式は巻き上げが行われないんですよね♪
m.ts10806

2020/06/06 08:22

tanakashouzouxさん 自身でも短いコード書いてみて挙動確かめてみると理解が深まると思います。 (個人的にはZuishinさんが試したコードが気になってます)
Zuishin

2020/06/06 08:28

m.ts10806 さん、宣言と式の違いを試したというか、変数に入れたらせっかく付けた名前が無かったことになったので、普通の const と function の巻き上げの違いになりました。
maisumakun

2020/06/06 08:33

> 変数に入れたらせっかく付けた名前が無かったことになったので 関数式で「function 名前()」のようにした場合の名前は、その関数の内側でだけ有効です(再帰呼び出ししたい場合に便利です)。
Zuishin

2020/06/06 08:36

maisumakun さんありがとうございます。内側からなら変数に入れる前に使うことができるということですね。おぼえておきます。
m.ts10806

2020/06/06 08:38

Zuishinさん maisumakun さん ありがとうございます。理解できました。
tanakashouzoux

2020/06/06 08:57

Zuishinさん maisumakunさん m.ts10806さん 皆さん本当にありがとうございますm(__)m それでなんですが、関数宣言文とは名前の付いた関数のことで、関数式とは無名関数ということなのでしょうか...??? 何が関数式化がいまいちピンと来ておりませんもので・・・
think49

2020/06/08 11:23

@tanakashouzouxさん 名前が付いてないのは、無名関数式 名前が付いているのは、名前付き関数式or関数宣言です。
guest

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
think49

総合スコア18189

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tanakashouzoux

2020/06/07 23:24

think49さん 大変貴重なご指導をありがとうございますm(__)m お陰様で朧気ながらに式と文の違いが分かってきました! これからも勉強が続けられそうです!!! 本当にありがとうございますm(__)m
guest

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
otn

総合スコア85766

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tanakashouzoux

2020/06/06 08:00

ご回答ありがとうございます!! 仰る通りです???? 間違ってしまっていたので質問を修正させて頂きましたm(__)m
otn

2020/06/06 08:02

> このコードだと関数宣言「文」を「変数に代入」していて してないと思いますが、どの記述のことですか?
tanakashouzoux

2020/06/06 08:06

何度もありがとうございますm(__)m 「const read = function() { };」の部分です 変数readに関数文「function(){};」を代入している気がするのですが・・・
otn

2020/06/06 08:19

ご自分で > 「function learn() {}」が「関数宣言文」、「function() {};」が「関数式」を表していると思うのですが、 と書いてますが?
tanakashouzoux

2020/06/06 08:26

otnさん何度もご丁寧にありがとうございますm(__)m すいません私の日本語力の無さで言いたいことを伝えきれておりませんでした???? 著者の方の考えでは「function learn() {}」を「関数宣言文」、「function() {};」を「関数式」と言いたいのだと思うのですが、私には「const read = function() {};」の部分について「function() {};」が「関数宣言文」で、その「文」を変数「read」に代入している気がして、「文が変数に代入できないのであれば、これはどう考えたら良いのだろう・・・」と思って悩んでおりました
otn

2020/06/06 08:35

> これはどう考えたら良いのだろう・・・」と思って悩んでおりました そのまま、 > 「function learn() {}」が「関数宣言文」、「function() {};」が「関数式」を表していると思うのですが、 と考えればいいです。
tanakashouzoux

2020/06/06 08:55

otnさんわざわざご回答を何度もありがとうございますm(__)m > 「function learn() {}」が「関数宣言文」、「function() {};」が「関数式」を表していると思うのですが、 と考えればいいです。 とのことですが、無名関数だと関数式、名前が付いている関数は関数宣言文と考えれば良いのでしょうか???
otn

2020/06/06 09:11

代入などしてない場合は関数宣言文で、代入するなど式の中で使われていれば関数式です。 名前の有無は本質的では無いです。 名前付きの関数式のサンプルを回答に追記しておきます。
tanakashouzoux

2020/06/06 10:46

わざわざサンプルまで追記頂きありがとうございますm(__)m 追記頂いた名前付き関数式、初めて拝見しました これはbar(3)がエラーで、foo(3)が6になると言うことは名前付き関数式はbarが存在しない時の計算と同じ様に計算すると言うことなのでしょうか?
otn

2020/06/06 10:53

> 名前付き関数式はbarが存在しない時の計算と同じ様に計算すると言うことなのでしょうか? 意味が取れません。 barという名前はその関数bar内でのみ有効で、外では未定義になります。
tanakashouzoux

2020/06/07 22:07

何度もご丁寧にありがとうございますm(__)m ご記述頂いた下記関数についてbar()は{}の中では使えるけれどそれ以外の場所からは使えない、foo(3)は下記関数の引数に3を代入した返り値ということで合ってますでしょうか? let foo = function bar(n) { return n<=1 ? 1 : n*bar(n-1); };
otn

2020/06/08 02:36

はい。
tanakashouzoux

2020/06/08 04:55

otnさん わざわざご回答いただきありがとうございます!!! 完全独学で勉強しておりまして、書籍やネットで調べてはいるつもりなのですが調べたことが合っているのか間違っているのかの判断が付かず、質問させて頂いておりました ですので、ご回答頂けて本当にありがたく思っておりますm(__)m
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問