MochiuWiki : SUSE, EC, PCB
検索
個人用ツール
ログイン
Toggle dark mode
名前空間
ページ
議論
表示
閲覧
ソースを閲覧
履歴を表示
JavaScriptの基礎 - 関数宣言と関数式のソースを表示
提供: MochiuWiki : SUSE, EC, PCB
←
JavaScriptの基礎 - 関数宣言と関数式
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループのいずれかに属する利用者のみが実行できます:
管理者
、new-group。
このページのソースの閲覧やコピーができます。
== 概要 == JavaScriptにおける関数は、ファーストクラスオブジェクト (First-class Object) として扱われる。<br> 変数への代入、他の関数への引数として渡すこと、関数の戻り値として返すことがすべて可能であり、これがJavaScriptの関数型プログラミングの基盤となっている。<br> <br> 関数を定義する方法として、主に関数宣言 (Function Declaration) と関数式 (Function Expression) の2種類がある。<br> この2つは構文だけでなく、ホイスティング (巻き上げ) の挙動において大きく異なる。<br> <br> 関数宣言は、<code>function</code> キーワードを文の先頭に記述する形式であり、スコープの先頭に関数全体が巻き上げられるため、宣言より前に呼び出すことができる。<br> 一方、関数式は変数に無名関数または名前付き関数を代入する形式であり、変数のホイスティングルールに従う。<br> <br> ES2015 (ES6) で導入されたデフォルト引数と残余引数も、関数定義の重要な機能である。<br> デフォルト引数を使用すると、引数が渡されなかった場合の初期値を関数の定義時に指定できる。<br> 残余引数は、可変長の引数を真の配列として受け取る仕組みであり、ES2015以前の <code>arguments</code> オブジェクトに比べて扱いやすい。<br> <br> Reactをはじめとする現代的なフレームワークでは、コンポーネントや高階関数、イベントハンドラとして関数を頻繁に使用する。<br> 関数宣言と関数式の特性を正確に理解することは、保守性の高いコードを書く上で不可欠である。<br> <br><br> == 関数宣言 == ==== 基本構文 ==== 関数宣言は、<code>function</code> キーワードに続けて関数名を記述する形式である。<br> 関数名は必須であり、省略することはできない。<br> <br> <syntaxhighlight lang="javascript"> function <関数名>(<引数1>, <引数2>) { // 処理 return <戻り値>; } </syntaxhighlight> <br> 使用例を以下に示す。<br> <br> <syntaxhighlight lang="javascript"> function add(a, b) { return a + b; } function greet(name) { return "こんにちは、" + name + "さん"; } console.log(add(3, 5)); // 8 console.log(greet("Alice")); // "こんにちは、Aliceさん" </syntaxhighlight> <br> 引数は複数指定できる。<br> 引数が無い場合は空の括弧 <code>()</code> を記述する。<br> <br> <syntaxhighlight lang="javascript"> // 引数無し function getCurrentTime() { return new Date().toLocaleTimeString(); } // 複数の引数 function createFullName(firstName, lastName, separator) { return firstName + separator + lastName; } console.log(getCurrentTime()); console.log(createFullName("太郎", "山田", " ")); // "太郎 山田" </syntaxhighlight> <br> ==== 戻り値 ==== return文は、関数の実行を終了して呼び出し元に値を返す。<br> <br> return文を記述しない場合 または 値を指定しない <code>return;</code> のみを記述した場合、関数は <code>undefined</code> を返す。<br> <br> <syntaxhighlight lang="javascript"> function noReturn() { const x = 1 + 2; // return文なし } function emptyReturn() { return; // 値なし } console.log(noReturn()); // undefined console.log(emptyReturn()); // undefined </syntaxhighlight> <br> 早期リターン (Early Return) を使用すると、条件に応じて関数を早期に終了させることができる。<br> この手法は、ネストを浅く保ち可読性を向上させる際に有効である。<br> <br> <syntaxhighlight lang="javascript"> function divide(a, b) { if (b === 0) { return null; // ゼロ除算の場合は早期リターン } return a / b; } console.log(divide(10, 2)); // 5 console.log(divide(10, 0)); // null </syntaxhighlight> <br> ==== ホイスティング ==== 関数宣言は、そのスコープ (関数スコープ または グローバルスコープ) の先頭に関数全体が巻き上げられる。<br> そのため、関数宣言より前のソースコードから呼び出すことができる。<br> <br> <syntaxhighlight lang="javascript"> // 宣言より前に呼び出しても正常に動作する console.log(add(2, 3)); // 5 function add(a, b) { return a + b; } </syntaxhighlight> <br> 上記の例では、JavaScriptエンジンにより内部的に以下に示すように処理される。<br> <br> <syntaxhighlight lang="javascript"> // JavaScriptエンジンが内部的に処理するイメージ function add(a, b) { return a + b; } console.log(add(2, 3)); // 5 </syntaxhighlight> <br> <code>var</code> によるホイスティングとの対比を以下に示す。<br> <code>var</code> 宣言はホイスティング時に <code>undefined</code> で初期化されるのに対し、関数宣言は関数本体ごと巻き上げられる。<br> <br> <syntaxhighlight lang="javascript"> console.log(typeof myFunc); // "function" (関数宣言: 関数本体が巻き上げられる) console.log(typeof myVar); // "undefined" (var: undefinedで初期化) function myFunc() {} var myVar = "hello"; </syntaxhighlight> <br><br> == 関数式 == ==== 基本構文 ==== 関数式は、変数に関数を代入する形式である。<br> 代入する関数は、名前を持たない無名関数式と名前を持つ名前付き関数式の2種類がある。<br> <br> 無名関数式の構文を以下に示す。<br> <br> <syntaxhighlight lang="javascript"> // 無名関数式 const <変数名> = function(<引数1>, <引数2>) { return <戻り値>; }; </syntaxhighlight> <br> 名前付き関数式の構文を以下に示す。<br> <br> <syntaxhighlight lang="javascript"> // 名前付き関数式 const <変数名> = function <関数名>(<引数1>, <引数2>) { return <戻り値>; }; </syntaxhighlight> <br> 使用例を以下に示す。<br> <br> <syntaxhighlight lang="javascript"> // 無名関数式 const multiply = function(a, b) { return a * b; }; // 名前付き関数式 const factorial = function calcFactorial(n) { if (n <= 1) return 1; return n * calcFactorial(n - 1); // 関数名で再帰呼び出し }; console.log(multiply(4, 5)); // 20 console.log(factorial(5)); // 120 </syntaxhighlight> <br> <u>関数式は変数に代入されるため、<code>const</code> または <code>let</code> で宣言することが推奨される。</u><br> <u>再代入の必要がない場合は <code>const</code> を使用する。</u><br> <br> ==== ホイスティングの違い ==== 関数式のホイスティング挙動は、代入先の変数の宣言キーワードに依存する。<br> <br> <code>const</code> または <code>let</code> で宣言した関数式は、Temporal Dead Zone (TDZ) の影響を受ける。<br> 宣言前にアクセスすると <u>ReferenceError</u> が発生する。<br> <br> <syntaxhighlight lang="javascript"> // const / let での宣言: TDZによりReferenceError console.log(greet("Alice")); // ReferenceError: Cannot access 'greet' before initialization const greet = function(name) { return "こんにちは、" + name; }; </syntaxhighlight> <br> <code>var</code> で宣言した関数式は、変数自体は <code>undefined</code> で初期化される。<br> 宣言前に呼び出すと <code>undefined</code> を関数として呼び出そうとするため、<u>TypeError</u> が発生する。<br> <br> <syntaxhighlight lang="javascript"> // var での宣言: undefinedで初期化されるためTypeError console.log(greet); // undefined (変数自体はホイスティングされる) console.log(greet("Alice")); // TypeError: greet is not a function var greet = function(name) { return "こんにちは、" + name; }; </syntaxhighlight> <br> 関数宣言と関数式のホイスティング挙動を比較した例を以下に示す。<br> <br> <syntaxhighlight lang="javascript"> // 関数宣言: 宣言前に呼び出し可能 console.log(declaredFunc()); // "関数宣言" function declaredFunc() { return "関数宣言"; } // 関数式 (const): 宣言前の呼び出しはReferenceError // console.log(expressionFunc()); // ReferenceError const expressionFunc = function() { return "関数式"; }; // 宣言後は通常通り呼び出せる console.log(expressionFunc()); // "関数式" </syntaxhighlight> <br> ==== 名前付き関数式 ==== 名前付き関数式は、関数式に名前を付けた形式である。<br> この名前は関数式の外部からはアクセスできず、関数自身のスコープ内でのみ参照可能である。<br> <br> 名前付き関数式が有用な場面として、再帰呼び出しとデバッグの2つが挙げられる。<br> <br> 再帰呼び出しでの使用例を以下に示す。<br> <br> <syntaxhighlight lang="javascript"> const fibonacci = function calcFibonacci(n) { if (n <= 1) return n; return calcFibonacci(n - 1) + calcFibonacci(n - 2); // 内部名で再帰呼び出し }; console.log(fibonacci(10)); // 55 // 関数名は外部からはアクセスできない // console.log(calcFibonacci(10)); // ReferenceError </syntaxhighlight> <br> デバッグでの有用性を以下に示す。<br> 無名関数式の場合、スタックトレースに関数名が表示されず、デバッグが困難になることがある。<br> 名前付き関数式を使用すると、スタックトレースに関数名が表示されて問題箇所の特定が容易になる。<br> <br> <syntaxhighlight lang="javascript"> // 無名関数式: スタックトレースに "<anonymous>" と表示される const anonymousFunc = function() { throw new Error("エラー発生"); }; // 名前付き関数式: スタックトレースに "namedFunc" と表示される const namedFunc = function namedFunc() { throw new Error("エラー発生"); }; </syntaxhighlight> <br><br> == 関数宣言と関数式の比較 == 下表に、関数宣言と関数式の主な違いを示す。<br> <br> <center> {| class="wikitable" |+ 関数宣言と関数式の比較 ! 観点 !! 関数宣言 !! 関数式 |- | ホイスティング || スコープ先頭に関数全体が巻き上げられる || 変数のホイスティングルールに従う |- | 宣言前の呼び出し || 可能 || 不可 (ReferenceError または TypeError) |- | 関数名 || 必須 || 省略可能 (無名関数式) |- | 構文の位置 || 文として記述 || 式として記述 (変数代入など) |- | 使用場面 || スコープ全体で使用する汎用的な関数 || 条件付き定義、コールバック、高階関数 |- | スコープ || 関数スコープ / グローバルスコープ || 変数の宣言キーワードに依存 |} </center> <br><br> == デフォルト引数 == ==== 基本構文 ==== デフォルト引数は、ES2015で導入された機能であり、引数が渡されなかった場合に使用する初期値を関数の定義時に指定できる。<br> <br> <syntaxhighlight lang="javascript"> function <関数名>(<引数1> = <デフォルト値1>, <引数2> = <デフォルト値2>) { // 処理 } </syntaxhighlight> <br> 使用例を以下に示す。<br> <br> <syntaxhighlight lang="javascript"> function greet(name = "ゲスト", greeting = "こんにちは") { return greeting + "、" + name + "さん"; } console.log(greet("Alice", "おはよう")); // "おはよう、Aliceさん" console.log(greet("Bob")); // "こんにちは、Bobさん" console.log(greet()); // "こんにちは、ゲストさん" </syntaxhighlight> <br> デフォルト引数がトリガーされるのは、引数に <code>undefined</code> が渡された場合のみである。<br> <code>null</code> を渡した場合はデフォルト値が適用されず、<code>null</code> がそのまま使用される。<br> <br> <syntaxhighlight lang="javascript"> function showValue(value = "デフォルト") { console.log(value); } showValue(undefined); // "デフォルト" (undefinedはデフォルト値をトリガー) showValue(null); // null (nullはデフォルト値をトリガーしない) showValue(0); // 0 (0もデフォルト値をトリガーしない) showValue(""); // "" (空文字もデフォルト値をトリガーしない) </syntaxhighlight> <br> ==== 式を使用したデフォルト値 ==== デフォルト引数には、リテラル値だけでなく任意の式を使用できる。<br> 前に定義したパラメータを参照することも可能である。<br> <br> <syntaxhighlight lang="javascript"> // 前のパラメータを参照 function createRectangle(width = 100, height = width * 2) { return { width, height }; } console.log(createRectangle()); // { width: 100, height: 200 } console.log(createRectangle(50)); // { width: 50, height: 100 } console.log(createRectangle(50, 80)); // { width: 50, height: 80 } </syntaxhighlight> <br> 関数呼び出しをデフォルト値として使用することも可能である。<br> デフォルト値の式は、関数が呼び出されるたびに評価される。(定義時ではなく呼び出し時に評価)<br> <br> <syntaxhighlight lang="javascript"> function getDefaultId() { return Math.floor(Math.random() * 1000); } function createUser(name, id = getDefaultId()) { return { name, id }; } console.log(createUser("Alice")); // { name: "Alice", id: <ランダムな値> } console.log(createUser("Bob")); // { name: "Bob", id: <別のランダムな値> } </syntaxhighlight> <br> ==== デフォルト引数導入前のパターン ==== ES2015以前は、デフォルト引数を実現するために論理OR演算子 (<code>||</code>) を用いたフォールバックパターンが広く使われていた。<br> <br> <syntaxhighlight lang="javascript"> // ES2015以前のパターン (||演算子によるフォールバック) function greet(name, greeting) { name = name || "ゲスト"; greeting = greeting || "こんにちは"; return greeting + "、" + name + "さん"; } </syntaxhighlight> <br> <u>しかし、このパターンには問題がある。</u><br> <u><code>||</code> 演算子は引数がFalsyな値 (<code>0</code>、<code>""</code>、<code>false</code> 等) の場合もデフォルト値を適用してしまう。</u><br> <br> <syntaxhighlight lang="javascript"> function setCount(count) { count = count || 10; // 問題: count が 0 の場合もデフォルト値が使われる console.log(count); } setCount(5); // 5 setCount(0); // 10 (意図しない動作: 0 は Falsy なので 10 が使われる) setCount(undefined); // 10 // ES2015のデフォルト引数を使用した場合 function setCountFixed(count = 10) { console.log(count); } setCountFixed(0); // 0 (正しく動作する: 0 はundefinedではないため) setCountFixed(undefined); // 10 </syntaxhighlight> <br><br> == 残余引数 == ==== 基本構文 ==== 残余引数 (Rest Parameters) は、ES2015で導入された機能であり、可変長の引数を真の配列として受け取る仕組みである。<br> パラメータ名の前に <code>...</code> (スプレッド構文) を付けて定義する。<br> <br> <syntaxhighlight lang="javascript"> function 関数名(...引数名) { // 引数名は真の配列として使用できる } </syntaxhighlight> <br> 残余引数は、必ず最後のパラメータとして定義しなければならない。<br> 残余引数の後にさらに引数を定義すると <u>SyntaxError</u> となる。<br> <br> <syntaxhighlight lang="javascript"> // 正しい例: 最後のパラメータとして定義 function fn(a, b, ...rest) { console.log(a); // 最初の引数 console.log(b); // 2番目の引数 console.log(rest); // 3番目以降の引数が配列として格納される } fn(1, 2, 3, 4, 5); // 1 // 2 // [3, 4, 5] // 誤りの例: 残余引数の後にパラメータを定義 (SyntaxError) // function badFn(...rest, last) { } </syntaxhighlight> <br> ==== 使用例 ==== 残余引数を使用した合計値の計算例を以下に示す。<br> 残余引数は真の配列であるため、<code>map</code>、<code>filter</code>、<code>reduce</code> 等のArrayメソッドを直接使用できる。<br> <br> <syntaxhighlight lang="javascript"> function sum(...numbers) { return numbers.reduce((total, num) => total + num, 0); } console.log(sum(1, 2, 3)); // 6 console.log(sum(1, 2, 3, 4, 5)); // 15 console.log(sum()); // 0 </syntaxhighlight> <br> * 通常のパラメータと残余引数を組み合わせた例 *: <syntaxhighlight lang="javascript"> function logMessage(level, ...messages) { const prefix = "[" + level.toUpperCase() + "]"; messages.forEach(function(msg) { console.log(prefix + " " + msg); }); } logMessage("info", "サーバ起動", "ポート3000で待機中"); // [INFO] サーバ起動 // [INFO] ポート3000で待機中 logMessage("error", "接続失敗"); // [ERROR] 接続失敗 </syntaxhighlight> *: <br> * 配列操作との組み合わせ例 *: <syntaxhighlight lang="javascript"> function filterAndDouble(threshold, ...numbers) { return numbers .filter(function(n) { return n > threshold; }) .map(function(n) { return n * 2; }); } console.log(filterAndDouble(3, 1, 2, 3, 4, 5)); // [8, 10] </syntaxhighlight> <br> ==== argumentsオブジェクトとの比較 ==== ES2015以前は、関数に渡された全ての引数にアクセスするために <code>arguments</code> オブジェクトを使用していた。<br> 残余引数は <code>arguments</code> オブジェクトの問題を解消する目的でも導入されている。<br> <br> <code>arguments</code> オブジェクトの使用例を以下に示す。<br> <br> <syntaxhighlight lang="javascript"> function oldSum() { let total = 0; for (let i = 0; i < arguments.length; i++) { total += arguments[i]; } return total; } console.log(oldSum(1, 2, 3)); // 6 // argumentsはArrayではないため、map等は直接使えない // arguments.reduce(...); // TypeError </syntaxhighlight> <br> 下表に、残余引数と <code>arguments</code> オブジェクトの違いを示す。<br> <br> <center> {| class="wikitable" |+ 残余引数とargumentsオブジェクトの比較 ! 観点 !! 残余引数 !! argumentsオブジェクト |- | 型 || 真のArrayインスタンス || 配列風オブジェクト (Array-like) |- | Arrayメソッド || map / filter / reduce 等を直接使用可能 || 直接使用不可 (Array.fromで変換が必要) |- | 対象の引数 || 名前付きパラメータを除いた残りの引数 || 関数に渡された全ての引数 |- | アロー関数 || 使用可能 || 使用不可 (アロー関数はargumentsを持たない) |- | 名前 || 任意の名前を付けられる || 常にargumentsという名前 |- | 導入バージョン || ES2015 (ES6) || ES1 (初期) |} </center> <br> <code>arguments</code> オブジェクトを配列に変換する方法と残余引数の比較を以下に示す。<br> <br> <syntaxhighlight lang="javascript"> // argumentsを配列に変換する方法 (ES2015以前) function oldStyle() { const args = Array.prototype.slice.call(arguments); // または const args2 = Array.from(arguments); return args.reduce(function(total, n) { return total + n; }, 0); } // 残余引数を使用した方法 (推奨) function newStyle(...numbers) { return numbers.reduce(function(total, n) { return total + n; }, 0); } console.log(oldStyle(1, 2, 3)); // 6 console.log(newStyle(1, 2, 3)); // 6 </syntaxhighlight> <br><br> == 関連情報 == * [[JavaScriptの基礎 - 変数宣言]] *: let / const / varの宣言方法、スコープ、ホイスティング * [[JavaScriptの基礎 - アロー関数]] *: アロー関数の構文、暗黙のreturn、レキシカルthis * [[JavaScriptの基礎 - クロージャ]] *: レキシカルスコープ、クロージャの仕組みと実用例 * [[JavaScriptの基礎 - コールバック関数]] *: コールバック関数、高階関数、タイマ、イベントリスナー <br><br> {{#seo: |title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki |keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,電気回路,電子回路,基板,プリント基板 |description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This page is {{PAGENAME}} in our wiki about electronic circuits and SUSE Linux |image=/resources/assets/MochiuLogo_Single_Blue.png }} __FORCETOC__ [[カテゴリ:Web]]
JavaScriptの基礎 - 関数宣言と関数式
に戻る。
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
Collapse