関数とは?
関数とは、ある一連の手続き(文の集まり)を1つの処理としてまとめる機能です。
一度定義した関数を呼び出すことで同じ処理を何回でも実行できます。つまり、関数を使いまわすことが出来るようになります。
関数宣言
functionからはじまる文は関数宣言と呼び、関数を定義するために使われます。
function 関数名(仮引数1, 仮引数2) {
return 関数の返り値;
}
const 関数の結果 = 関数名(引数1, 引数2);
console.log(関数の結果);
関数の構成要素
関数は次の4つの要素で構成されています。
関数名 - 関数の名前。関数を呼び出す際に使われる名前です。利用できる名前は変数名と同じです。.
仮引数 - 関数の呼び出し時に渡される実引数を受け取るための変数です。カンマで区切ると、複数の仮引数を記述できます。
関数の中身(実際の処理) - {}
の間に関数の処理を書きます。
関数の返り値 - 関数の処理内でreturn文を記述すると、関数の実行結果を戻り値として呼び出し元に返すことが出来ます。
関数の特徴
- 宣言した関数は、
関数名()
のように関数名にカッコをつけることで呼び出せます。
- 関数を引数と共に呼ぶ際は、
関数名(引数1, 引数2)
とし、引数が複数ある場合は,(カンマ)
で区切ります。
- 関数の処理としてreturn文が実行されると、関数内のそれ以降の処理は行われません。
- 関数が値を返す必要がない場合は、戻り値を省略できますが、return文そのものを省略することも出来ます。どちらの場合も未定義の値undefinedが返ります。
function fn() {
return;
}
console.log(fn());
関数の引数
JavaScriptでは、関数に定義した仮引数の個数と実際に呼び出したときの実引数の個数が違っても、関数を呼び出せます。
仮引数に比べて、呼び出し時の実引数の数が少ないとき
定義した関数の仮引数よりも呼び出し時の実引数が少ない場合、余った仮引数にはundefinedという値が代入されます。
function echo(x) {
return x;
}
console.log(echo(1));
console.log(echo());
function argumentsToArray(x, y) {
return [x, y];
}
console.log(argumentsToArray(1, 2));
console.log(argumentsToArray(1));
呼び出し時の実引数の数が少ないときの対応策:[ES2015] デフォルト引数
デフォルト引数(デフォルトパラメータ)は、仮引数に対応する実引数が渡されていない場合に、デフォルトで代入される値を指定できます。 仮引数に対して仮引数 = デフォルト値
という構文で、仮引数ごとにデフォルト値を指定できます。
function 関数名(仮引数1 = デフォルト値1, 仮引数2 = デフォルト値2) {
}
- 実引数が渡されなかった場合にデフォルト値が仮引数に代入されます。
- ES2015でデフォルト引数が導入されるまでは、OR演算子(
||
)を使ったデフォルト値の指定がよく利用されていました。しかし、OR演算子(||
)を使ったデフォルト値の指定にはfalsyな値を渡すとデフォルト値が入ってしまうという問題点があります。
- falsyな値とは、真偽値へと変換するとfalseとなる次のような値のことです。
- false
- undefined
- null
- 0
- 0n
- NaN
- ""(空文字列)
function addPrefix(text, prefix) {
const pre = prefix || "デフォルト:";
return pre + text;
}
console.log(addPrefix("こんにちは"));
console.log(addPrefix("こんにちは", ""));
console.log(addPrefix("こんにちは", 0));
console.log(addPrefix("こんにちは", "カスタム:"));
- 上記から分かるように、2つ目の実引数を渡さなかった場合以外に、falsyな値(空文字や0など)を渡した場合でもデフォルト値が代入されてしまいます。
これが本当に0や空文字を渡したい場合だとデフォルト値が入るのは問題です。
このようにfalsyな値を実引数として渡した場合にデフォルト値が代入される事が意図した挙動なのかどうかがわかりにくくバグに繋がりやすいです。
- ES2020から導入されたNullish coalescing演算子(
??
)を利用することでも、 OR演算子(||
)の問題を避けつつデフォルト値を指定できます。
function addPrefix(text, prefix) {
const pre = prefix ?? "デフォルト:";
return pre + text;
}
console.log(addPrefix("文字列"));
console.log(addPrefix("文字列", ""));
console.log(addPrefix("文字列", "カスタム:"));
OR演算子(||
)とNullish coalescing演算子(??
)の違い
|
記述方法 |
評価方法 |
OR演算子 |
A || B |
左辺のオペランドがfalsyな値の場合に右辺のオペランドを評価する。 |
Nullish coalescing演算子 |
A ?? B |
左辺のオペランドがnullまたはundefinedの場合に右辺のオペランドを評価する。 |
仮引数に比べて、呼び出し時の実引数の数が多いとき
関数の仮引数に対して実引数の個数が多い場合、あふれた実引数は単純に無視されます。
function add(x, y) {
return x + y;
}
console.log(add(1, 3));
console.log(add(1, 3, 5));
- 上記から実引数を3つ以上渡しても無視されてしまうため
add(1, 3)
とadd(1, 3, 5)
の結果は同じになります。
呼び出し時の実引数の数が多いときの対応策:可変長引数
関数において引数の数が固定ではなく、任意の個数分引数を受け取りたい場合可変長引数を使います。
可変長引数を使うと関数宣言時に受け取る実引数の個数をあらかじめ固定する必要がなく、呼び出し時に個数の制限なく実引数を渡すことができるようになります。
可変長引数を実現するためには、Rest parametersかargumentsという特殊な変数を利用します。
可変長引数を実現する方法①[ES2015] Rest parameters
Rest parametersは、仮引数名の前に...
をつけた仮引数のことで、残余引数とも呼ばれます。 Rest parametersには、関数に渡された値が配列として代入されます。Rest parametersを使うことで実引数を何個でも受け取ることができるようになります。
function fn(...args) {
return console.log(args);
}
fn("a", "b", "c");
fn("a", "b", "c", "d", "e", "f");
fn("a", "b", "c", "d", "e", "f", "g", "h", "i");
Rest parametersは、通常の仮引数と組み合わせても定義できます。 ほかの仮引数と組み合わせる際には、必ずRest parametersを末尾の仮引数として定義する必要があります。
function fn(arg1, ...restArgs) {
console.log(arg1);
console.log(restArgs);
}
fn("a", "b", "c");
fn();
- 1番目の実引数はarg1に代入され、残りの実引数がrestArgsに配列としてまとめて代入されます。
RestParametersの似ている記述にSpread構文がありますが、使い方に違いがあるので注意しましょう。
|
構文の意味 |
使い方 |
RestParameters |
実引数を任意の個数受け取りたい時に実引数を配列としてまとめて受け取る構文です。Rest parametersを関数の仮引数に指定して使います。この時仮引数の前に... をつけます。 |
...仮引数 |
Spread構文 |
関数呼び出し時に、渡したい実引数を配列としてまとめて渡すことができます。実際には、実引数として渡された配列の展開した要素が関数の仮引数に渡されます。関数を呼び出す際、配列名の前に... をつけて使います。 |
...配列 |
function fn(x, y, z) {
console.log(x);
console.log(y);
console.log(z);
}
const array = [1, 2, 3];
fn(...array);
fn(array[0], array[1], array[2]);
可変長引数を実現する方法②arguments
可変長引数を扱う方法として、argumentsという関数の中でのみ参照できる特殊な変数があります。 argumentsは関数に渡された引数の値がすべて入ったArray-likeなオブジェクトです。
Array-likeなオブジェクトは、配列のようにインデックスで要素へアクセスできますが、Arrayのメソッドは利用できないという特殊なオブジェクトです。
function fn() {
console.log(arguments[0]);
console.log(arguments[1]);
console.log(arguments[2]);
console.log(arguments);
}
fn("a", "b", "c");
argumentsの問題点
上記の問題点から可変長引数が必要な場合はarguments変数よりも、Rest parametersで実装するようにしましょう。
[ES2015] 関数の引数と分割代入
関数の引数においても分割代入が利用できます。 分割代入はオブジェクトや配列からプロパティを取り出し、変数として定義し直す構文です。
分割代入を使わない場合
function printUserId(user) {
console.log(user.id);
}
const user = {
id: 42
};
printUserId(user);
分割代入を使う場合(オブジェクトvar)
function printUserId({ id }) {
console.log(id);
}
const user = {
id: 42
};
printUserId(user);
- 関数の引数における分割代入では、仮引数に定義したい変数名を定義し、実引数として渡すオブジェクトから対応するプロパティを代入します。
const id = user.id;
- 関数の引数における分割代入は、オブジェクトだけではなく配列についても利用できます。
関数の引数における分割代入(配列var)
function print([first, second, third]) {
console.log(first);
console.log(second);
console.log(third);
}
const array = [1, 2];
print(array);
関数はオブジェクト
JavaScriptでは、関数は関数オブジェクトとも呼ばれ、オブジェクトの一種です。
関数はただのオブジェクトとは異なり、関数名に()
をつけることで、関数としてまとめた処理を呼び出すことができます。
一方で、()
をつけて呼び出されなければ、関数をオブジェクトとして参照できる他、変数へ代入したり、関数の引数として渡すなど値として扱うことが可能です。
🌟このように関数が値として扱えることを、ファーストクラスファンクション(第一級関数)と呼びます。
関数を値として扱うことができると..?
- 関数を変数に代入できる
- 関数の引数として渡すことができる(コールバック関数)
関数を値として定義する方法①関数式
関数式とは、関数を値として変数へ代入している式のことを言います。 関数宣言は文でしたが、関数式では関数を値として扱っています。
const 関数名 = function(仮引数) {
return 関数の返り値;
};
function 関数名(仮引数) {
return 関数の返り値;
}
- 関数式は変数名で参照できるため、"関数名"を省略できます。
- functionキーワードを使った関数宣言では"関数名"は省略できません。
- 関数式では、名前を持たない関数(匿名関数・無名関数)を変数に代入できます。
- 関数式で定義した関数は、一般的に変数などに代入するため名前をつけない匿名関数として定義されることが多いです。
しかし一方で、関数式で定義した関数に名前をつけた場合、定義した関数名は関数の中からしか呼べなくなります。
関数を値として定義する方法②[ES2015] Arrow Function
関数式にはfunctionキーワードを使った方法以外に、Arrow Functionと呼ばれる書き方があります。 名前のとおり矢印のような=>
(アロー記号)を使い、匿名関数を定義する構文です。
const 関数名 = (仮引数) => {
return 関数の返す値;
};
Arrow Functionの省略記法
const fnA = () => { };
const fnB = (x) => { };
const fnC = x => { };
const fnD = (x, y) => { };
const mulA = x => { return x * x; };
const mulB = x => x * x;
Arrow Functionの特徴
- 名前をつけることができない(常に匿名関数)
- thisが静的に決定できる
- functionキーワードに比べて短く書くことができる
- newできない(コンストラクタ関数ではない)
- arguments変数を参照できない(functionキーワードに比べて、人による解釈や実装の違いが生まれにくい)
functionキーワードの関数式でコールバック関数を定義した場合
const array = [1, 2, 3];
const doubleArray = array.map(function(value) {
return value * 2;
});
console.log(doubleArray);
- mapメソッドの引数としてfunctionキーワードを使った匿名関数(コールバック関数)を定義しています。
- mapメソッドは、順番にarray配列の要素をコールバック関数に渡し、そのコールバック関数が返した値を新しい配列にして返します。
- 今回の場合、1,2,3と順番に要素の値が渡され、コールバック関数で要素の値を2倍にして戻り値として返します。
mapメソッドは戻り値を受け取り、要素を2倍にした値を要素とする新たな配列を作成して返します。
Arrow Functionでコールバック関数を定義した場合
const array = [1, 2, 3];
const doubleArray = array.map(value => value * 2);
console.log(doubleArray);
- mapメソッドの引数としてArrow Functionを使った匿名関数(コールバック関数)を定義しています。
- Arrow Functionでは関数の処理が1つの式だけである場合に、return文を省略し暗黙的にその式の評価結果をreturnの返り値とします。
- Arrow Functionの仮引数が1つである場合は
()
を省略できます。
- Arrow Functionを使う方がコードの見通しを良く記述できます。
上記の特徴からArrow Functionは、人による解釈や実装の違いが生まれにくく、thisの問題の多くを解決できるという利点があるため、積極的にArrow Functionを使うようにしましょう。
Arrow Functionを使うことに問題がある場合にのみfunctionキーワードを使いましょう。
同じ名前の関数宣言は上書きされる
関数宣言で定義した関数は、引数の数や型の違いで区別されることなく、関数の名前でのみ区別されます。 そのため、同じ関数名を複数回宣言した場合には、後ろで宣言された同じ関数名によって上書きされます。
(TypeScriptで宣言した関数は、名前以外にも引数の数や型の違いで区別されます。そのため、同じ関数名を複数回宣言した場合にも上書きされることなく、引数の数や型が異なる同じ関数が共存する状態になります。)
function fn(x) {
return `最初の関数 x: ${x}`;
}
function fn(x, y) {
return `最後の関数 x: ${x}, y: ${y}`;
}
console.log(fn(2, 10));
ただし、同じ関数名で複数の関数を定義することは基本的に意味がないため避けるべきです。
コールバック関数
関数はファーストクラスであるため、匿名関数を関数の引数(値)として渡すことができます。 ある関数やメソッドの引数として渡される関数のことをコールバック関数と呼びます。
一方、コールバック関数を引数として使う関数やメソッドのことを高階関数と呼びます。
function 高階関数(コールバック関数) {
コールバック関数();
}
コールバック関数は非同期処理においてもよく利用されます。
メソッド
JavaScriptではオブジェクトのプロパティが関数である場合にそれをメソッドと呼びます。JavaScriptにおいて、関数とメソッドの機能的な違いはありません。
一般的にはメソッドも含めたものを関数と言い、関数宣言などとプロパティである関数を区別する場合にメソッドと呼びます。
const obj = {
method1: function(引数) {
console.log('Hello World');
},
method2: (引数) => {
console.log('Good Night');
}
};
obj.method1();
obj.method2();
obj.method1
プロパティとobj.method2
プロパティがメソッドです。
const obj = {};
obj.method = function() {
return "this is method";
};
console.log(obj.method());
- 空のオブジェクトobjを定義後、関数を値とするmethodプロパティをobjに代入すると、
obj.method
プロパティがメソッドになります。
- メソッドを呼び出す場合は、関数呼び出しと同様に
オブジェクト名.メソッド名()
と書くことで呼び出せます。
[ES2015] メソッドの短縮記法
ES2015からは、メソッドの短縮記法が追加されました。短縮記法はオブジェクトのメソッド定義だけではなく、クラスのメソッドを定義する共通の書き方になります。
const obj = {
key: 'value',
method() {
return "this is method";
}
};
console.log(obj.method());
console.log(obj.key);
- オブジェクトリテラルの中で
メソッド名(){ /*メソッドの処理*/ }
と記述するとメソッドの短縮記法が使えます。
メソッドまとめ
名前 |
関数 |
メソッド |
関数宣言(function fn(){}) |
✔ |
x |
関数式(const fn = function(){}) |
✔ |
✔ |
Arrow Function(const fn = () => {}) |
✔ |
✔ |
メソッドの短縮記法(const obj = { method(){} }) |
x |
✔ |
メソッドの定義方法(3種類)
const obj = {
method1: function() {
},
method2: () => {
},
method3() {
}
};
参考
JSPrimer-関数と宣言