関数の基本
JavaScriptの関数は、一連の手順やタスクを実行するための再利用可能なコードブロックです。関数は、入力値(引数)を取り込み、処理を実行し、結果を返します。
関数は、以下のように定義することができます。
function 関数名(引数1, 引数2, ) {
// 関数が実行する処理
return 結果; // 結果を返す場合
}
例えば、以下の関数を定義してみます。
function addNumbers(a, b) {
return a + b;
}
この関数は、2つの引数を受け取り、それらを加算して結果を返します。関数を呼び出すには、以下のようにします。
let result = addNumbers(3, 5);
console.log(result); // 結果: 8
このように、関数を使用することで、同じ処理を何度も実行する必要がある場合に、コードの再利用性を高めることができます。
引数の特徴と仮引数と実引数
引数には以下の特徴があります。
-
関数の中で変数と同じように使用できる
-
命名規則は変数の命名規則に準拠する
-
不必要なら引数は省略できる
-
引数を複数個設定する場合は、カンマ(,)で区切る
引数には仮引数(Parameters)と実引数(Arguments)があります。仮引数は関数の定義の中で、関数が呼び出されたときに受け取る引数を定義する変数のことです。仮引数は、関数内で参照できます。実引数は関数を呼び出すときに、関数に渡す値のことです。実引数は、関数内で仮引数に代入されます。
仮引数と実引数の違いは、主に以下の点にあります。
-
定義の場所:仮引数は、関数の定義の中で宣言されます。一方、実引数は、関数を呼び出す時に関数名の後ろに括弧を付けて渡されます。
-
役割:仮引数は、関数内で使用する変数としての役割を持ちます。一方、実引数は、関数に必要なデータや値を提供する役割を持ちます。
-
型:仮引数は、関数定義の中で型を指定することができます。一方、実引数の型は、渡される値によって決まります。
-
数量:仮引数の数は、関数の定義によって決まります。一方、実引数の数は、関数を呼び出す際に指定されます。
例えば、以下のような関数があった場合、x
とy
が仮引数であり、add
関数を呼び出す際に渡される値2
と3
が実引数です。
function add(x, y) {
return x + y;
}
let result = add(2, 3); // xに2が、yに3が渡される
console.log(result); // 結果: 5
注意点として、仮引数と実引数の数が異なる場合には、実引数が少ない場合は不足した引数の値がundefined
になり、逆に実引数が多い場合は、余分な引数は無視されます。
可変長引数
JavaScriptでは、可変長引数として、引数の数が可変的な関数を定義することができます。可変長引数を扱う場合、関数定義で引数リストに … を使って宣言することで実現できます。この ...
を「スプレッド構文」と呼びます。
以下は、可変長引数を扱う関数の例です。
function sum(args) {
let total = 0;
for(let arg of args) {
total += arg;
}
return total;
}
console.log(sum(1, 2, 3)); // 結果: 6
console.log(sum(4, 5, 6, 7, 8)); // 結果: 30
この例では、sum
関数の引数リストに ...args
と書かれています。これにより、sum
関数に渡された引数が可変長であっても、全ての引数が args
という名前の配列にまとめられます。そして、for...of
文を使って args
配列の要素を一つずつ取り出し、合計を計算しています。argsの部分は他の名前でも構いません。
このように、可変長引数を扱うことで、引数の数が可変的な関数を定義することができます。可変長引数を使うことで、引数の数が多くなっても、コードの記述量を減らすことができます。ただし、可変長引数を使う場合は、関数内で引数の個数がどの程度になるか確認することができないため、適切な引数のバリデーションを行う必要があります。
arguments
ES6以前のJavaScriptでは、可変長引数を実現するためにargumentsが使われていました。argumentsは配列に似た特殊なオブジェクトで、関数定義で仮引数を設定せず、関数呼び出しで実引数を渡しても関数内で取得できる機能を提供します。
以下は、argumentsオブジェクトの使用例です。この例では、forループでargumentsオブジェクトの長さを数えています。
function myFunc() {
let count = 0;
for (let i = 0; i < arguments.length; i++) {
count++;
}
console.log(`argumentsの数は${count}個です。`);
}
myFunc('hello', 123, true);
// 結果: argumentsの数は3個です。
しかし、argumentsオブジェクトには、いくつかの問題があります。
-
argumentsオブジェクトは、配列ではなく、オブジェクトであるためArrayクラスの多くのメソッドが使えない。
-
argumentsオブジェクトは、ES6で導入された可変長引数の機能により、必要性が低下している。
-
argumentsオブジェクトは、strictモードで使用できないため、安全でないことがある。
これらの理由から、argumentsオブジェクトはあまり使用されなくなってきています。
戻り値
戻り値とは、関数が実行された結果として返す値のことです。関数の戻り値は、return文を使用して定義されます。returnに続く式の結果が関数から返されます。戻り値がない場合は、undefinedが返されます。
function myFunc(a, b) {
return a + b;
}
const result = myFunc(2, 3);
console.log(result);
// 結果: 5
この例では、myFunc()関数が2つの引数を受け取り、それらを足し合わせた結果をreturnを使って返します。 戻り値の特徴は以下の通りです。
-
関数は複数の値を返すことができませんが、オブジェクトや配列といった複合データ型を使って、複数の値を含んだ単一の戻り値を返すことができます。
-
returnに値を設定しない場合は、undefinedが返ります。
-
returnを実行すると、その時点で関数は終了します。returnより後にあるコードは実行されません。
-
returnを省略すると、関数は暗黙的にundefinedを返します。
-
returnにより戻り値が返される場合でも、必ずしも実行文で戻り値を受け取る必要はありません。
関数式による関数定義
関数の定義は代入演算子を使い、変数に対して関数式を代入することでも可能です。
JavaScriptの関数式による関数定義は、以下です。
const 関数名 = function(引数1, 引数2, ) {
// 関数の処理を記述する
return 戻り値; // 戻り値がない場合は省略可能
};
例えば、次のように関数式による関数定義を行うことができます。
const greet = function(name) {
console.log("Hello, " + name + "!");
};
greet("Taro"); // "Hello, Taro!"
関数式は、変数に代入するため、その変数がスコープ外になると、関数もスコープ外になります。また、関数内で自身を呼び出すことはできません。
関数宣言で定義しても、関数式で定義しても使用方法や機能に大きな違いはありません。しかし、細部で挙動の違いがあります。
関数宣言で定義を行った場合、関数の宣言文の前で実行文を記述可能ですが、関数式ではエラーとなります。
// 関数宣言の場合
sampleFunc(); // "これはサンプルです。"
function sampleFunc() {
console.log("これはサンプルです。"):
}
// 関数式の場合
sampleFunc2(); // エラーが発生(Uncaught ReferenceError: sampleFunc2 is not defined)
const sampleFunc2 = function() {
console.log("あいうえお");
}
Functionコンストラクタ
JavaScriptには、Functionコンストラクタを使って、文字列形式で関数を定義することができます。しかし、通常の関数宣言や関数式のように関数を定義する方法よりも、より危険な方法であり、セキュリティ上の問題を引き起こす可能性があるため、使用は推奨されません。以下は、Functionコンストラクタを使った関数定義の例です。
var 関数名 = new Function("引数1", "引数2", "...", "関数の処理");
// 例
var greet = new Function("name", "console.log('Hello, ' + name + '!');");
greet("Taro"); // "Hello, Taro!"と出力される
上記のように、Functionコンストラクタは、引数として文字列形式で関数の引数と関数本体を与えることで、新しい関数を定義します。しかし、この方法は以下のような欠点があります。
-
コードの可読性が低くなる
-
エラーが発生した場合にデバッグが困難になる
-
セキュリティ上の問題がある
したがって、通常の関数宣言や関数式を使用することをお勧めします。また、関数定義には、引数として渡される文字列を直接実行するという点で、eval()関数と類似しており、同様にセキュリティ上の問題があるため、eval()関数と同様に慎重に使用する必要があります。
即時関数
JavaScriptには、即時関数と呼ばれる関数の宣言と同時に実行される関数があります。以下は、即時関数の定義と使い方の例です。
(function() {
// 関数の処理を記述する
})();
上記のように、関数を括弧で囲んで、その後に()を付けることで、関数の即時実行を行うことができます。この方法は、一時的なスコープを作成することができ、変数や関数を隠蔽して、グローバルスコープとの競合を避けることができます。
また、即時関数に引数を渡すことができ、以下のように関数に引数を渡して、関数内で利用することができます。
(function(x, y) {
console.log(x + y);
})(3, 4); // 7と出力される
即時関数は、主に以下のような場面で利用されます。
-
グローバルスコープを汚染しないようにするため
-
即時関数を使用することで、一時的なスコープを作成することができ、変数や関数を隠蔽して、グローバルスコープとの競合を避けることができます。
-
-
外部ライブラリとの競合を避けるため
-
即時関数を使用することで、外部ライブラリと同じ変数名を使用しても、競合を避けることができます。
-
-
モジュールパターンを実現するため
-
即時関数を使用することで、外部に公開する必要のない変数や関数を隠蔽し、モジュールパターンを実現することができます。
-
以上が、JavaScriptの即時関数の使い方と、利用する意味についての説明です。即時関数は、JavaScriptでよく使われるテクニックの1つであり、理解しておくと、より効率的なコードを書くことができます。
デフォルト引数
JavaScriptの関数には、引数にデフォルト値を指定できます。これをデフォルト引数と呼びます。デフォルト引数を使うことで、関数呼び出し時に引数が省略された場合、デフォルト値が自動的に設定されるため、エラーを防ぐことができます。
以下は、デフォルト引数を使用した関数の例です。
function greet(name = 'World') {
console.log(`Hello, ${name}!`);
}
greet(); // Hello, World!と出力される
greet('Taro'); // Hello, Taro!と出力される
上記の例では、greet()関数の引数nameにデフォルト値として’World’を指定しています。したがって、引数なしでgreet()関数を呼び出した場合、nameは’World’というデフォルト値が設定されます。また、greet(‘Taro’)というように引数を指定した場合は、nameに指定された値(’Taro’)が設定されます。
複数の引数にデフォルト値を指定する場合は、以下のように書きます。
function calculatePrice(price, tax = 0.08, discount = 0) {
return (price + (price * tax)) - (price * discount);
}
console.log(calculatePrice(1000)); // 1080と出力される
console.log(calculatePrice(1000, 0.1)); // 1100と出力される
console.log(calculatePrice(1000, 0.1, 0.05)); // 1045と出力される
上記の例では、calculatePrice()関数の引数taxとdiscountにそれぞれデフォルト値として0.08と0を指定しています。これらの引数は省略可能であり、省略した場合にはデフォルト値が設定されます。
デフォルト引数は、関数のパラメーターに初期値を与えるときに便利です。引数が設定されなかった場合に予期せぬエラーを回避できるため、プログラムの安定性を向上させることができます。
また、デフォルト引数の値が設定されるのは、仮引数に渡ってきた値がundefinedの場合に限ります。nullの場合は設定は行われない点に注意して下さい。
引数が多い場合はオブジェクトで渡す
JavaScriptの関数には、多くの引数を受け取る場合に、オブジェクトを使用して引数をまとめる方法があります。これにより、関数呼び出し時に引数を明示的に指定する必要がなくなり、関数内で引数を処理する際にも見やすくなります。
以下は、オブジェクトを使用した関数の例です。
function printUserInfo(user) {
console.log(`名前: ${user.name}`);
console.log(`年齢: ${user.age}`);
console.log(`性別: ${user.gender}`);
}
const user = {
name: '山田太郎',
age: 25,
gender: '男性'
};
printUserInfo(user);
上記の例では、printUserInfo()関数の引数にオブジェクトuserを指定しています。userオブジェクトには、name、age、genderというプロパティが含まれており、それぞれ名前、年齢、性別を表しています。
printUserInfo()関数内で、userオブジェクトの各プロパティにアクセスして、その値を出力しています。
このように、オブジェクトを使用することで、引数の順序や個数を意識する必要がなくなり、関数呼び出し時にわかりやすくなります。また、オブジェクトを使用することで、引数の追加や削除が容易になります。
ただし、オブジェクトを使用する場合は、関数内でオブジェクトの各プロパティにアクセスする必要があるため、コードが少し複雑になることがあります。また、引数を明示的に指定しないため、コードの可読性が低下する可能性があり、適切に使用する必要があります。
引数にオブジェクトを使う際の注意点
JavaScriptでは、関数の引数にオブジェクトを渡すことができます。しかし、オブジェクトを渡す場合には、そのオブジェクトが参照渡し(メモリアドレス)であることに注意する必要があります。
参照渡しとは、オブジェクトを渡す場合、そのオブジェクトの参照が関数内に渡されるため、関数内でオブジェクトのプロパティを変更すると、元のオブジェクトも変更されるという挙動です。
以下は、参照渡しの例です。
function changeUserName(user) {
user.name = '鈴木花子';
}
const user = {
name: '山田太郎',
age: 25,
gender: '男性'
};
changeUserName(user);
console.log(user.name); // 出力結果: 鈴木花子
上記の例では、changeUserName()関数にオブジェクトuserを渡しています。関数内でuserオブジェクトのnameプロパティを変更しています。その後、user.nameを出力しています。
出力結果は、関数内で変更されたuserオブジェクトのnameプロパティの値である「鈴木花子」が表示されます。
このように、オブジェクトを関数の引数に渡す場合には、元のオブジェクトが変更される可能性があるため、注意が必要です。関数内でオブジェクトのプロパティを変更したくない場合には、オブジェクトのコピーを作成してから渡すか、関数内で新しいオブジェクトを作成して処理するようにすると良いでしょう。
コールバック関数
コールバック関数を説明する前に、まず、関数とは何かを確認します。関数とは実行可能なオブジェクトです。オブジェクト{ }と異なるのは、実行可能である点だけです。オブジェクトである以上、関数にもプロパティやメソッドを追加することができます。
function sampleFunc() { }
sampleFunc.lastName = "山田"; // プロパティの追加が可能
sampleFunc.gender = function() { // メソッドの追加も可能
console.log("男性");
}
console.log(sampleFunc.lastName); // '山田'
sampleFunc.gender(); // 男性
この関数の性質を前置きした上で、コールバック関数を解説します。
JavaScriptにおいて、関数はオブジェクトの一種であり、変数に代入することも可能です。この性質を利用して、関数を引数として別の関数に渡すことができます。この渡された関数を「コールバック関数」と呼びます。
コールバック関数は、ある処理が完了した後に呼び出される関数で、処理の途中で実行されます。コールバック関数を利用することで、非同期処理やイベント処理などが実現されています。
以下は、コールバック関数の例です。
function calc(a, b, callback) {
const result = a + b;
callback(result);
}
function printResult(result) {
console.log(result);
}
calc(10, 20, printResult); // 出力結果: 30
上記の例では、calc()関数に2つの数値とコールバック関数を引数として渡しています。calc()関数内で、2つの数値を足し合わせた結果を変数resultに代入し、その結果を引数としてコールバック関数を呼び出しています。
次に、printResult()関数が定義されており、この関数は渡された引数をコンソールに出力するだけの簡単な関数です。
最後に、calc()関数を呼び出しています。このとき、calc()関数に10と20の数値とprintResult()関数を渡しています。calc()関数内で処理が完了した後、コールバック関数として渡されたprintResult()関数が実行され、計算結果である30がコンソールに出力されます。
このように、コールバック関数を利用することで、処理の流れを自由に制御することができます。また、非同期処理やイベント処理などにも利用されているため、JavaScriptにおいて非常に重要な概念となっています。
無名関数を使ったコールバック関数
JavaScriptにおいて、無名関数を使ったコールバック関数は非常によく使われます。無名関数とは、名前のない関数のことです。通常の関数と同様に定義することができますが、名前を省略することができます。
以下は、無名関数を使ったコールバック関数の例です。
function calc(a, b, callback) {
const result = a + b;
callback(result);
}
calc(10, 20, function(result) {
console.log(result); // 出力結果: 30
});
上記の例では、calc()関数に2つの数値と無名関数を引数として渡しています。calc()関数内で、2つの数値を足し合わせた結果を変数resultに代入し、その結果を引数として渡された無名関数を呼び出しています。
次に、無名関数が定義されており、この関数は渡された引数をコンソールに出力するだけの簡単な関数です。この関数は名前がなく、function(result)のように引数を定義するだけです。
最後に、calc()関数を呼び出しています。このとき、calc()関数に10と20の数値と、無名関数を渡しています。calc()関数内で処理が完了した後、渡された無名関数が実行され、計算結果である30がコンソールに出力されます。
このように、無名関数を使ったコールバック関数は、関数を定義する必要がないため、簡潔でスッキリとしたコードを書くことができます。また、無名関数は関数式で定義することができるため、コードの読みやすさを向上させることもできます。
アロー関数
アロー関数はES6で導入された新しい関数の書き方です。通常の関数宣言に比べてシンプルな書き方ができるため、コードの可読性を向上させることができます。
以下は、アロー関数の書き方の例です。
// 通常の関数宣言
function add(a, b) {
return a + b;
}
// アロー関数
const add = (a, b) => a + b;
アロー関数は、引数の個数や関数の本文の行数により、省略できる度合いが変わってきます。以下がそれぞれのケースにおける記述方法です。アロー関数と無名関数での両方の書き方を併記したので、比較してみて下さい。
// ①引数がない場合
// アロー関数
() => { 関数の本文; }
// 無名関数
function() { 関数の本文; }
// ②引数の1つの場合
// アロー関数
引数 => { 関数の本文; }
// 無名関数
function(引数) { 関数の本文; }
// ③引数が複数の場合
// アロー関数
(引数1, 引数2, ) => { 関数の本文; }
// 無名関数
function(引数1, 引数2, ) { 関数の本文; }
// ④関数の本文が1行の場合
// アロー関数
(引数1, 引数2, ) => 関数の本文;
// 無名関数
function(引数1, 引数2, ) {
return 関数の本文;
}
// ⑤関数の本文が複数行の場合
// アロー関数
(引数1, 引数2, ) => {
関数の本文;
return 戻り値;
}
// 無名関数
function(引数1, 引数2, ) {
関数の本文;
return 戻り値;
}
// ⑥関数の本文が1行、且つ戻り値がオブジェクトの場合
// アロー関数
(引数1, 引数2, ) => ( { プロパティ1: 値1, プロパティ2: 値2 } )
// 無名関数
function(引数1, 引数2, ) {
return { プロパティ1: 値1, プロパティ2: 値2 }
}
無名関数との違い
アロー関数と無名関数には書き方だけではなく、挙動にも違いがあります。
キーワード | 無名関数 | アロー関数 |
---|---|---|
thisを持つ | ○ | × |
argumentsを持つ | ○ | × |
newでのインスタンス化 | ○ | × |
prototypeを持つ | ○ | × |
コメント