JavaScript では、関数は「魔法の言語構造」ではなく、特別な種類の値です。
以前に使用した構文は、関数宣言と呼ばれます。
関数sayHi() { アラート( "こんにちは" ); }
関数を作成するには、 Function Expressionと呼ばれる別の構文があります。
これにより、任意の式の途中に新しい関数を作成できます。
例えば:
let SayHi = function() { アラート( "こんにちは" ); };
ここでは、変数sayHi
値を取得しているのがわかります。これは、 function() { alert("Hello"); }
として作成された新しい関数です。 function() { alert("Hello"); }
.
関数の作成は代入式 ( =
の右側) のコンテキストで行われるため、これはFunction Expressionです。
function
キーワードの後には名前がないことに注意してください。関数式では名前を省略できます。
ここではすぐに変数に代入しているため、これらのコード サンプルの意味は同じです。「関数を作成し、それを変数sayHi
に入れる」ということです。
後ほど説明するさらに高度な状況では、関数が作成されてすぐに呼び出されるか、後で実行されるようにスケジュールされ、どこにも保存されず、匿名のままになることがあります。
繰り返しますが、関数がどのように作成されたとしても、関数は値です。上記の両方の例では、 sayHi
変数に関数を格納しています。
alert
使用してその値を出力することもできます。
関数sayHi() { アラート( "こんにちは" ); } アラート(sayHi); // 関数コードを表示します
sayHi
後に括弧がないため、最後の行では関数が実行されないことに注意してください。関数名の言及によって関数が実行されるプログラミング言語もありますが、JavaScript はそうではありません。
JavaScript では関数は値なので、値として扱うことができます。上記のコードは、その文字列表現、つまりソース コードを示しています。
確かに、関数は、 sayHi()
のように呼び出すことができるという意味で、特別な値です。
しかし、それでも価値があるのです。したがって、他の種類の値と同様にそれを扱うことができます。
関数を別の変数にコピーできます。
function SayHi() { // (1) 作成 アラート( "こんにちは" ); } let func =sayHi; // (2) コピー 関数(); // こんにちは // (3) コピーを実行します (動作します)。 こんにちは(); // こんにちは // これでもまだ動作します (なぜ動作しないのでしょう)
上記で何が起こるかを詳しく説明します。
関数宣言(1)
は関数を作成し、それをsayHi
という名前の変数に入れます。
行(2)
はそれを変数func
にコピーします。もう一度注意してください: sayHi
の後に括弧はありません。存在する場合、 func = sayHi()
は、関数sayHi
自体ではなく、関数sayHi()
の呼び出しの結果をfunc
に書き込みます。
これで、関数はsayHi()
とfunc()
両方として呼び出すことができます。
関数式を使用して、最初の行でsayHi
宣言することもできます。
let SayHi = function() { // (1) 作成 アラート( "こんにちは" ); }; let func =sayHi; // ...
すべてが同じように機能します。
なぜ最後にセミコロンがあるのでしょうか?
なぜ関数式にセミコロンが付いているのか疑問に思われるかもしれません;
ただし、関数宣言では次のことは行われません。
関数sayHi() { // ... } let SayHi = function() { // ... };
答えは簡単です。ここでは関数式が代入ステートメント内でfunction(…) {…}
として作成されますlet sayHi = …;
。セミコロン;
ステートメントの最後に置くことをお勧めしますが、これは関数構文の一部ではありません。
セミコロンは、 let sayHi = 5;
のように、より単純な代入に使用されます。 、関数の割り当てにも使用されます。
関数を値として渡したり、関数式を使用したりする例をさらに見てみましょう。
3 つのパラメータを持つ関数ask(question, yes, no)
を作成します。
question
質問の本文
yes
答えが「はい」の場合に実行される関数
no
答えが「いいえ」の場合に実行される関数
関数はquestion
をし、ユーザーの答えに応じて、 yes()
またはno()
を呼び出す必要があります。
関数 ask(質問、はい、いいえ) { if (確認(質問)) はい() それ以外の場合はいいえ(); } 関数 showOk() { alert( "あなたは同意しました。" ); } 関数 showCancel() { alert( "実行をキャンセルしました。" ); } // 使用法: 関数 showOk、showCancel が引数として渡され、問い合わせられます。 ask(「同意しますか?」、showOk、showCancel);
実際には、このような関数は非常に便利です。実際のask
と上記の例の主な違いは、実際の関数は、単純なconfirm
よりも複雑な方法を使用してユーザーと対話することです。ブラウザでは、このような関数は通常、見栄えの良い質問ウィンドウを描画します。しかし、それはまた別の話です。
ask
の引数showOk
およびshowCancel
コールバック関数または単にコールバックと呼ばれます。
考え方としては、関数を渡し、後で必要に応じて「呼び戻される」ことを期待するということです。この場合、 showOk
「はい」の応答のコールバックとなり、 showCancel
「いいえ」の応答のコールバックになります。
関数式を使用すると、同等の短い関数を作成できます。
関数 ask(質問、はい、いいえ) { if (確認(質問)) はい() それ以外の場合はいいえ(); } 聞く( "同意しますか?"、 function() {alert("あなたは同意しました。"); }、 function() {alert("実行がキャンセルされました。"); } );
ここでは、関数はask(...)
呼び出しの内部で宣言されています。これらには名前がないため、 anonymousと呼ばれます。このような関数は、 ask
の外部ではアクセスできません (変数に割り当てられていないため)。しかし、それがここで必要なことです。
このようなコードはスクリプトに非常に自然に表示され、JavaScript の精神に基づいています。
関数とは「動作」を表す値です
文字列や数値などの通常の値はデータを表します。
機能はアクションとして認識できます。
これを変数間で渡して、必要なときに実行できます。
関数宣言と式の主な違いを定式化してみましょう。
まず構文、つまりコード内でそれらを区別する方法です。
関数宣言:メイン コード フローで別のステートメントとして宣言された関数。
// 関数の宣言 関数 sum(a, b) { a + b を返します。 }
関数式:式内または別の構文構造内で作成される関数。ここでは「代入式」 =
の右辺に関数を作成しています。
// 関数式 let sum = function(a, b) { a + b を返します。 };
より微妙な違いは、関数が JavaScript エンジンによって作成される場合です。
関数式は、実行が到達したときに作成され、その瞬間からのみ使用できます。
実行フローが代入の右側に渡されると、 let sum = function…
– さあ、関数が作成され、これから使用 (割り当て、呼び出しなど) できるようになります。
関数宣言は異なります。
関数宣言は、定義される前に呼び出すことができます。
たとえば、グローバル関数宣言は、どこにあるかに関係なく、スクリプト全体で表示されます。
それは内部アルゴリズムによるものです。 JavaScript がスクリプトを実行する準備をするとき、まずスクリプト内でグローバル関数宣言を検索し、関数を作成します。これは「初期化段階」と考えることができます。
すべての関数宣言が処理された後、コードが実行されます。したがって、これらの機能にアクセスできます。
たとえば、これは機能します:
SayHi("ジョン"); // こんにちは、ジョン 関数sayHi(名前) { alert( `こんにちは、${name}` ); }
関数宣言sayHi
は、JavaScriptがスクリプトを開始する準備をしているときに作成され、スクリプト内のどこにでも表示されます。
…それが関数式の場合は、機能しません。
SayHi("ジョン"); // エラー! let SayHi = function(name) { // (*) もう魔法はありません alert( `こんにちは、${name}` ); };
関数式は、実行が到達すると作成されます。これは(*)
行でのみ発生します。遅すぎる。
関数宣言のもう 1 つの特別な機能は、そのブロック スコープです。
厳密モードでは、関数宣言がコード ブロック内にある場合、そのブロック内のどこにでも表示されます。しかし、その外側ではありません。
たとえば、実行時に取得するage
変数に応じて関数welcome()
宣言する必要があると想像してみましょう。そして、しばらくしてから使用する予定です。
関数宣言を使用すると、意図したとおりに動作しません。
let age = プロンプト("あなたの年齢は何ですか?", 18); // 条件付きで関数を宣言します if (年齢 < 18) { 関数 welcome() { アラート("こんにちは!"); } } それ以外 { 関数 welcome() { alert("こんにちは!"); } } // ...後で使用します いらっしゃいませ(); // エラー: ようこそが定義されていません
これは、関数宣言は、それが存在するコード ブロック内でのみ表示されるためです。
別の例を次に示します。
年齢 = 16 としましょう。 // 16 を例に挙げます if (年齢 < 18) { いらっしゃいませ(); // (実行) // | 関数 welcome() { // | アラート("こんにちは!"); // | 関数宣言が可能 } // | 宣言されているブロック内のどこでも // | いらっしゃいませ(); // / (実行) } それ以外 { 関数 welcome() { alert("こんにちは!"); } } // ここで中括弧がなくなりました。 // したがって、その内部で行われた関数宣言は表示されません。 いらっしゃいませ(); // エラー: ようこそが定義されていません
if
の外側にwelcome
表示するにはどうすればよいでしょうか?
正しいアプローチは、関数式を使用し、 if
の外で宣言され、適切な可視性を持つ変数にwelcome
を割り当てることです。
このコードは意図したとおりに動作します。
let age = プロンプト("あなたの年齢は何ですか?", 18); 歓迎しましょう。 if (年齢 < 18) { ようこそ = function() { アラート("こんにちは!"); }; } それ以外 { ようこそ = function() { alert("こんにちは!"); }; } いらっしゃいませ(); // もういいよ
あるいは、疑問符演算子を使用してさらに単純化することもできます?
:
let age = プロンプト("あなたの年齢は何ですか?", 18); ようこそ = (年齢 < 18) ? function() {alert("こんにちは!"); } : function() {alert("こんにちは!"); }; いらっしゃいませ(); // もういいよ
関数宣言と関数式を選択するのはどのような場合ですか?
経験則として、関数を宣言する必要がある場合、最初に考慮するのは関数宣言の構文です。このような関数を宣言する前に呼び出すことができるため、コードを編成する方法がより自由になります。
コード内でfunction f(…) {…}
を検索するほうが、 let f = function(…) {…};
よりも簡単なので、読みやすさにも優れています。 。関数宣言はより「目を引く」ものになります。
…しかし、何らかの理由で関数宣言が適さない場合、または条件付き宣言が必要な場合 (例を見たばかりです)、関数式を使用する必要があります。
関数は値です。これらは、コードの任意の場所で割り当て、コピー、または宣言できます。
関数がメイン コード フロー内で別のステートメントとして宣言されている場合、それは「関数宣言」と呼ばれます。
関数が式の一部として作成された場合、それは「関数式」と呼ばれます。
関数宣言は、コード ブロックが実行される前に処理されます。それらはブロック内のどこにでも表示されます。
関数式は、実行フローが関数式に到達したときに作成されます。
ほとんどの場合、関数を宣言する必要がある場合は、関数宣言が宣言自体の前に表示されるため、関数宣言の方が望ましいと考えられます。これにより、コード構成の柔軟性が高まり、通常は読みやすくなります。
したがって、関数宣言がタスクに適合しない場合にのみ関数式を使用する必要があります。この章ではその例をいくつか見てきましたが、今後さらに多くの例を見ていきます。