多くの場合、スクリプトのさまざまな場所で同様のアクションを実行する必要があります。
たとえば、訪問者がログインしたとき、ログアウトしたとき、あるいは他の場所で見栄えの良いメッセージを表示する必要があります。
関数はプログラムの主要な「構成要素」です。これにより、コードを反復せずに何度も呼び出すことができます。
これまでにalert(message)
、 prompt(message, default)
、 confirm(question)
などの組み込み関数の例を見てきました。ただし、独自の関数を作成することもできます。
関数を作成するには、関数宣言を使用できます。
次のようになります。
関数 showMessage() { alert( '皆さんこんにちは!' ); }
function
キーワードが最初にあり、次にfunction の名前が続き、次に括弧内のパラメーターのリスト (上の例ではカンマで区切られ、空になっています。後で例を参照します)、最後に同じく名前が付けられた関数のコードが続きます。中括弧の間の「関数本体」。
関数名(パラメータ1, パラメータ2, ... パラメータN) { // 体 }
新しい関数は、 showMessage()
という名前で呼び出すことができます。
例えば:
関数 showMessage() { alert( '皆さんこんにちは!' ); } showMessage(); showMessage();
showMessage()
呼び出しにより、関数のコードが実行されます。ここではメッセージが 2 回表示されます。
この例は、関数の主な目的の 1 つであるコードの重複を避けることを明確に示しています。
メッセージやメッセージの表示方法を変更する必要がある場合は、コードを 1 か所、つまりメッセージを出力する関数を変更するだけで十分です。
関数内で宣言された変数は、その関数内でのみ表示されます。
例えば:
関数 showMessage() { let message = "こんにちは、JavaScript です!"; // ローカル変数 アラート(メッセージ); } showMessage(); // こんにちは、JavaScript です! アラート(メッセージ); // <-- エラー!変数は関数に対してローカルです
関数は外部変数にもアクセスできます。次に例を示します。
userName = 'ジョン' とします。 関数 showMessage() { let message = 'Hello, ' + userName; アラート(メッセージ); } showMessage(); // こんにちは、ジョン
この関数は外部変数に完全にアクセスできます。それを変更することもできます。
例えば:
userName = 'ジョン' とします。 関数 showMessage() { ユーザー名 = "ボブ"; // (1) 外部変数を変更しました let message = 'Hello, ' + userName; アラート(メッセージ); } アラート( ユーザー名 ); // 関数呼び出し前のジョン showMessage(); アラート( ユーザー名 ); // ボブ、値は関数によって変更されました
外部変数は、ローカル変数がない場合にのみ使用されます。
同じ名前の変数が関数内で宣言されている場合、その変数は外側の変数をシャドウします。たとえば、以下のコードでは、関数はローカルuserName
を使用します。外側のものは無視されます。
userName = 'ジョン' とします。 関数 showMessage() { ユーザー名 = "ボブ" にします; // ローカル変数を宣言します let message = 'Hello, ' + userName; // ボブ アラート(メッセージ); } // 関数は独自の userName を作成して使用します showMessage(); アラート( ユーザー名 ); // ジョン、変更なし、関数は外部変数にアクセスしませんでした
グローバル変数
上記のコードの外側のuserName
など、関数の外側で宣言された変数は、 global と呼ばれます。
グローバル変数は、(ローカル変数によってシャドウされない限り) どの関数からも参照できます。
グローバル変数の使用を最小限に抑えることをお勧めします。最新のコードにはグローバルがほとんどないか、まったくありません。ほとんどの変数は関数内に存在します。ただし、プロジェクト レベルのデータを保存するのに役立つ場合もあります。
パラメータを使用して任意のデータを関数に渡すことができます。
以下の例では、関数にはfrom
とtext
という 2 つのパラメーターがあります。
function showMessage(from, text) { // パラメータ: from, text アラート(from + ': ' + テキスト); } showMessage('アン', 'こんにちは!'); // アン: こんにちは! (*) showMessage('アン', "どうしたの?"); // アン: どうしたの? (**)
この関数が行(*)
および(**)
で呼び出されると、指定された値がfrom
およびtext
ローカル変数にコピーされます。その後、関数はそれらを使用します。
もう 1 つの例を示します。関数from
変数を取得し、それを関数に渡します。注意: 関数はfrom
変更されますが、関数は常に値のコピーを取得するため、変更は外部には見られません。
関数 showMessage(from, text) { から = '*' + から + '*'; // 「from」をより見栄え良くする alert( from + ': ' + text ); } = "アン" からみましょう。 showMessage(from, "こんにちは"); // *アン*: こんにちは // "from" の値は同じです。関数はローカル コピーを変更しました アラート(から); // アン
値が関数パラメータとして渡される場合、その値は引数とも呼ばれます。
言い換えれば、これらの用語をストレートに言うと、次のようになります。
パラメーターは、関数宣言の括弧内にリストされている変数です (これは宣言時の用語です)。
引数は、関数が呼び出されたときに関数に渡される値です (呼び出し時の条件です)。
パラメータをリストした関数を宣言し、引数を渡して呼び出します。
上の例では、「関数showMessage
2 つのパラメータで宣言され、次に 2 つの引数 ( from
と"Hello"
で呼び出されます。」と言えるかもしれません。
関数が呼び出されても引数が指定されていない場合、対応する値はundefined
になります。
たとえば、前述の関数showMessage(from, text)
単一の引数を使用して呼び出すことができます。
showMessage("アン");
それは間違いではありません。このような呼び出しでは"*Ann*: undefined"
が出力されます。 text
の値が渡されないため、 undefined
なります。
=
を使用して、関数宣言のパラメーターにいわゆる「デフォルト」(省略された場合に使用する) 値を指定できます。
function showMessage(from, text = "テキストが指定されていません") { alert( from + ": " + text ); } showMessage("アン"); // アン: テキストは指定されていません
text
パラメータが渡されない場合、値"no text given"
が取得されます。
パラメーターが存在する場合、デフォルト値もジャンプしますが、次のように厳密にはundefined
と等しくなります。
showMessage("アン"、未定義); // アン: テキストは指定されていません
ここで"no text given"
は文字列ですが、パラメーターが欠落している場合にのみ評価され、割り当てられる、より複雑な式にすることもできます。したがって、次のようなことも可能です。
function showMessage(from, text = anotherFunction()) { // anotherFunction() はテキストが指定されていない場合にのみ実行されます // 結果はテキストの値になります }
デフォルトパラメータの評価
JavaScript では、デフォルトのパラメーターは、それぞれのパラメーターを指定せずに関数が呼び出されるたびに評価されます。
上記の例では、 text
パラメーターが指定されている場合、 anotherFunction()
はまったく呼び出されません。
一方、 text
が欠落している場合は、毎回独立して呼び出されます。
古い JavaScript コードのデフォルトパラメータ
数年前、JavaScript はデフォルト パラメーターの構文をサポートしていませんでした。そこで人々は他の方法を使ってそれらを指定しました。
今日では、古い文字でそれらを見つけることができます。
たとえば、 undefined
を明示的にチェックします。
関数 showMessage(from, text) { if (テキスト === 未定義) { text = 'テキストが指定されていません'; } alert( from + ": " + text ); }
…または||
使用します。オペレーター:
関数 showMessage(from, text) { // text の値が false の場合は、デフォルト値を割り当てます // これは text == "" がテキストがないことと同じであると仮定します テキスト = テキスト || 「テキストが指定されていません」; ... }
関数宣言後の後の段階でパラメータにデフォルト値を割り当てることが合理的な場合があります。
関数の実行中にパラメーターが渡されたかどうかは、 undefined
と比較することで確認できます。
関数 showMessage(テキスト) { // ... if (text === unknown) { // パラメータが欠落している場合 text = '空のメッセージ'; } アラート(テキスト); } showMessage(); // 空のメッセージ
…あるいは、 ||
使用することもできます。オペレーター:
関数 showMessage(テキスト) { // テキストが未定義または偽の場合は、「空」に設定します テキスト = テキスト || '空の'; ... }
最新の JavaScript エンジンは null 合体演算子をサポートします??
、 0
などのほとんどの偽の値を「正常」と見なすほうがよいでしょう。
関数 showCount(カウント) { // count が未定義または null の場合は、「unknown」を表示します アラート(カウント ?? "不明"); } showCount(0); // 0 showCount(null); // 未知 showCount(); // 未知
関数は、結果として呼び出し元のコードに値を返すことができます。
最も単純な例は、2 つの値を合計する関数です。
関数 sum(a, b) { a + b を返します。 } 結果 = sum(1, 2); とします。 アラート(結果); // 3
ディレクティブreturn
関数の任意の場所に置くことができます。実行がそれに到達すると、関数は停止し、値が呼び出し側コードに返されます (上記のresult
に割り当てられます)。
1 つの関数内でreturn
が多数発生する場合があります。例えば:
関数 checkAge(年齢) { if (年齢 >= 18) { true を返します。 } それ以外 { returnconfirm('親の許可は得ていますか?'); } } let age = プロンプト('あなたは何歳ですか?', 18); if ( checkAge(年齢) ) { alert( 'アクセス許可' ); } それ以外 { alert( 'アクセスが拒否されました' ); }
値を指定せずにreturn
使用することもできます。これにより、関数はすぐに終了します。
例えば:
関数 showMovie(年齢) { if ( !checkAge(年齢) ) { 戻る; } alert( "映画を見せます" ); // (*) // ... }
上記のコードで、 checkAge(age)
がfalse
返した場合、 showMovie
alert
に進みません。
空のreturn
がある関数、または空の戻り値がない関数はundefined
返します
関数が値を返さない場合、それはundefined
を返す場合と同じです。
function doNothing() { /* 空 */ } alert( doNothing() === 未定義 ); // 真実
空のreturn
もreturn undefined
と同じです。
関数 doNothing() { 戻る; } alert( doNothing() === 未定義 ); // 真実
return
と値の間に改行を追加しないでください
return
の長い式の場合、次のように別の行に記述したくなるかもしれません。
戻る (some + 長い + 式 + または + 何でも * f(a) + f(b))
JavaScript はreturn
後にセミコロンがあると想定しているため、これは機能しません。これは次と同じように機能します。
戻る; (some + 長い + 式 + または + 何でも * f(a) + f(b))
したがって、実質的には空のリターンになります。
返された式を複数行にわたって折り返す場合は、 return
と同じ行から開始する必要があります。または、少なくとも次のように開き括弧をそこに置きます。
戻る ( いくつか + 長い + 式 + または + 何でも * f(a) + f(b) )
そしてそれは私たちが期待したとおりに機能します。
機能とはアクションです。したがって、彼らの名前は通常動詞です。コードを読んだ人が関数の動作を理解できるように、簡潔かつできるだけ正確に、関数の動作を説明する必要があります。
アクションを漠然と説明する言葉の接頭辞を付けて関数を開始することが広く行われています。接頭語の意味についてはチーム内で合意が必要です。
たとえば、 "show"
で始まる関数は通常、何かを表示します。
…で始まる関数
"get…"
– 値を返します。
"calc…"
– 何かを計算します。
"create…"
– 何かを作成し、
"check…"
– 何かをチェックしてブール値などを返します。
そのような名前の例:
showMessage(..) // メッセージを表示します getAge(..) // 年齢を返します(何らかの方法で取得します) calcSum(..) // 合計を計算し、結果を返します createForm(..) // フォームを作成します (通常はフォームを返します) checkPermission(..) // 権限をチェックし、true/falseを返します
プレフィックスを付けると、関数名を一目見るだけで、その関数がどのような種類の処理を実行し、どのような値を返すかを理解できます。
1 つの機能 – 1 つのアクション
関数は、その名前から示唆されることを正確に実行する必要があり、それ以上のことは実行しません。
2 つの独立したアクションは、通常は一緒に呼び出される場合でも、通常は 2 つの関数に値します (その場合、これら 2 つを呼び出す 3 番目の関数を作成できます)。
このルールに違反する例をいくつか示します。
getAge
– 年齢に関するalert
を表示する場合は問題があります (get のみにする必要があります)。
createForm
– ドキュメントを変更してフォームを追加する場合は問題があります (フォームを作成して返すだけです)。
checkPermission
– access granted/denied
メッセージを表示する場合は問題があります (チェックを実行して結果を返すだけです)。
これらの例では、プレフィックスの一般的な意味を想定しています。あなたとあなたのチームは他の意味について自由に同意できますが、通常は大きな違いはありません。いずれにしても、接頭辞の意味、接頭辞付き関数で何ができるのか、何ができないのかをしっかりと理解しておく必要があります。同じ接頭辞を持つ関数はすべてルールに従う必要があります。そしてチームは知識を共有する必要があります。
超短い関数名
非常に頻繁に使用される関数には、非常に短い名前が付いていることがあります。
たとえば、jQuery フレームワークは$
を使用して関数を定義します。 Lodash ライブラリには、 _
という名前のコア関数があります。
これらは例外です。一般に、関数名は簡潔でわかりやすいものにする必要があります。
関数は短く、実行する処理が 1 つだけである必要があります。それが大きい場合は、その関数をいくつかの小さな関数に分割する価値があるかもしれません。このルールに従うのはそれほど簡単ではない場合もありますが、それは間違いなく良いことです。
別の関数はテストとデバッグが簡単なだけでなく、その存在自体が素晴らしいコメントです。
たとえば、以下の 2 つの関数showPrimes(n)
比較してください。それぞれがn
までの素数を出力します。
最初のバリアントではラベルを使用します。
関数 showPrimes(n) { nextPrime: for (let i = 2; i < n; i++) { for (j = 2; j < i; j++) { if (i % j == 0) nextPrime を続行します。 } アラート( i ); // 素数 } }
2 番目のバリアントでは、追加関数isPrime(n)
使用して素数性をテストします。
関数 showPrimes(n) { for (let i = 2; i < n; i++) { if (!isPrime(i)) 続行; アラート(i); // 素数 } } 関数 isPrime(n) { for (let i = 2; i < n; i++) { if ( n % i == 0) は false を返します。 } true を返します。 }
2 番目のバリエーションの方が理解しやすいですね。コード部分の代わりに、アクションの名前 ( isPrime
) が表示されます。このようなコードを自己記述型と呼ぶことがあります。
したがって、再利用するつもりがなくても関数を作成できます。コードを構造化し、読みやすくします。
関数宣言は次のようになります。
関数名(パラメータ、区切り、カンマ) { /* コード */ }
パラメータとして関数に渡された値は、そのローカル変数にコピーされます。
関数は外部変数にアクセスできます。しかし、それは内側から外側に向かってのみ機能します。関数の外側のコードはローカル変数を認識しません。
関数は値を返すことができます。そうでない場合、結果はundefined
なります。
コードをクリーンで理解しやすくするために、関数内では外部変数ではなく主にローカル変数とパラメーターを使用することをお勧めします。
パラメータを取得せずに外部変数を副作用として変更する関数よりも、パラメータを取得して処理し、結果を返す関数の方が常に理解しやすいです。
関数の命名:
名前は、関数が何を行うのかを明確に説明する必要があります。コード内で関数呼び出しを見たとき、適切な名前を付けると、その関数が何を実行し、何を返すのかがすぐに理解できます。
関数はアクションであるため、関数名は通常、口頭で付けられます。
create…
、 show…
、 get…
、 check…
などのよく知られた関数プレフィックスが多数存在します。それらを使用して、関数が何を行うかを示すヒントを与えます。
関数はスクリプトの主要な構成要素です。基本を説明したので、実際に作成して使用できるようになります。しかし、それはまだ道の始まりにすぎません。私たちは何度もそれらに戻り、その高度な機能をさらに深く掘り下げていきます。
重要度: 4
次の関数は、パラメータage
が18
より大きい場合にtrue
返します。
それ以外の場合は、確認を求め、その結果を返します。
関数 checkAge(年齢) { if (年齢 > 18) { true を返します。 } それ以外 { // ... returnconfirm('親は許可しましたか?'); } }
else
が削除された場合、関数の動作は異なりますか?
関数 checkAge(年齢) { if (年齢 > 18) { true を返します。 } // ... returnconfirm('親は許可しましたか?'); }
これら 2 つの亜種の動作に違いはありますか?
違いはありません!
どちらの場合も、 return confirm('Did parents allow you?')
if
条件が false の場合に正確に実行されます。
重要度: 4
次の関数は、パラメータage
が18
より大きい場合にtrue
返します。
それ以外の場合は、確認を求め、その結果を返します。
関数 checkAge(年齢) { if (年齢 > 18) { true を返します。 } それ以外 { returnconfirm('親は許可しましたか?'); } }
これを書き直して、同じことを実行しますが、 if
使用せずに 1 行で実行します。
checkAge
の 2 つのバリアントを作成します。
疑問符演算子を使用しますか?
OR の使用||
疑問符演算子'?'
の使用:
関数 checkAge(年齢) { 戻る (年齢 > 18) ? true :confirm('親は許可しましたか?'); }
OR の使用||
(最も短いバリアント):
関数 checkAge(年齢) { 戻る (18 歳以上) || confirm('親は許してくれましたか?'); }
ここでは、 age > 18
以上の括弧は必要ないことに注意してください。これらは読みやすさを向上させるために存在します。
重要性: 1
2 つの数値a
とb
の最小値を返す関数min(a,b)
を作成します。
例えば:
min(2, 5) == 2 min(3, -1) == -1 min(1, 1) == 1
if
使用した解決策:
関数 min(a, b) { if (a < b) { を返します。 } それ以外 { bを返します。 } }
疑問符演算子'?'
を使用した解決策:
関数 min(a, b) { a < b を返しますか? a:b; }
PS 等価a == b
の場合、何を返すかは問題ではありません。
重要度: 4
x
のn
乗を返す関数pow(x,n)
を作成します。言い換えると、 x
n
回乗算して結果を返します。
pow(3, 2) = 3 * 3 = 9 pow(3, 3) = 3 * 3 * 3 = 27 pow(1, 100) = 1 * 1 * ...* 1 = 1
x
とn
の入力を求める Web ページを作成し、 pow(x,n)
の結果を表示します。
デモを実行する
PS このタスクでは、関数はn
の自然値 ( 1
から最大の整数) のみをサポートする必要があります。
関数 pow(x, n) { 結果 = x とします。 for (let i = 1; i < n; i++) { 結果 *= x; } 結果を返します。 } let x = プロンプト("x?", ''); let n = プロンプト("n?", ''); if (n < 1) { alert(`Power ${n} はサポートされていません。正の整数を使用してください`); } それ以外 { アラート( pow(x, n) ); }