プログラミングをする上で必須となる機能の1つに、関数があります。
既存で用意されているメソッドなども含め、関数は切っても切り離せない重要な機能となります。
プログラミングにおける基本的な機能でありながら、使用する言語によって関数の書き方や振る舞いは様々です。
そこで今回は、JavaScriptにおける関数の主な仕様や使い方などについて、詳しく解説していきたいと思います。
関数を扱う上で関連性の高い this キーワードやスコープなどについても解説するので、ぜひ参考にしてみてください。
関数(function)とは?
関数(function)とは、関連する複数の処理を1つにまとめ、名前を付けて管理することができる機能のことを言います。
宣言した関数は、関数名を記述することで様々な場所から繰り返し呼び出し可能となります。
そのため、複数箇所で共通する処理などを関数としてまとめることで、記述するコードの量を減らすことができます。
処理内容の修正や追加が必要になった際にも、関数内のコードを変更するだけで済むようになるため、作業の効率性や安全性を高めることができるメリットがあります。
function の使い方
JavaScriptで関数を定義する方法は、主に以下の3つがあります。
- 関数宣言
- 関数式
- コンストラクタ
それぞれの定義方法について見ていきましょう。
function キーワードで関数を宣言する
1つ目は、関数宣言によって定義する方法です。
定義する際の基本構文は、以下のように記述します。
【基本構文】
function 関数名(引数1, 引数2, …) {
//処理内容
}functionキーワードの後に関数名を記述し、()内に関数に渡したい引数を指定します。(必要ない場合は()のみ記述)
{} のブロック内に関数としてまとめたい処理を記述することで、対象の関数を呼び出した際にその内容を実行することができます。
また、関数宣言にのみ適用される仕様として、定義した関数の内容が巻き上げ (ホイスティング) されるという大きな特徴があります。
ホイスティングとは、関数や変数などの宣言タイミングが、コードの実行前に自動で先頭へと移動される仕様のことを言います。
ホイスティングに伴う変数や関数の振る舞いは宣言方法によって異なりますが、関数宣言の場合は定義内容全てが宣言とともにホイスティングされるため、宣言タイミングに関わらずコードのどの位置からでも呼び出して使用することができます。
関数式で関数を定義する
2つ目は、関数式(関数リテラル) で定義する方法です。
関数式とは、関数を変数に代入する値として扱う式のことを言います。
JavaScriptにおいては、関数もデータ型の1つであるため、変数に代入することが可能となっています。
関数式を使用する際は、以下のような形式で記述します。
【基本構文】
const sample = function(引数1, 引数2) {
// 処理内容
}基本構文は関数宣言とほとんど同じ書き方をしますが、関数式の場合は関数名を記述する必要がありません。
ただし、任意で関数名を付けることも可能で、その場合はブロック内でのみ自身を呼び出すための関数名として使用できます。
【サンプルコード】
const hoge = function fuga() {
fuga(); // 使用可能
}
fuga(); // 使用不可、エラーが発生するまた、関数式の場合は定義内容の巻き上げが行われないため、宣言時より前の場所で変数を呼び出した場合にはエラーが発生します。
Function() コンストラクターで関数を定義する
3つ目は、コンストラクタを使用して定義する方法です。
先ほど解説したように、JavaScriptにおける関数は一種のデータ型のため、Functionオブジェクトのコンストラクタを経由して関数を定義することができます。
その場合は、以下のように記述します。
【基本構文】
const sample = new Function(引数1, 引数2, …, 処理内容)コンストラクタの引数の末尾に渡した値が関数の処理内容となり、それ以前に渡した値は定義する関数の仮引数名となります。
コンストラクタを使用して生成された Functionオブジェクトは、関数が評価されるごとに構文解析が行われる関係上、解析されるのが一度のみである関数宣言や関数式と比べてパフォーマンスが落ちる傾向にあります。
そのため、基本的には使用をなるべく避けた方が良いとされる方法でもあります。
関数(function)の書き方
それでは、簡単なサンプルをもとに、実際の定義方法を確認してみましょう。
基本的な関数定義の例
まずは、関数宣言で定義する例です。
【サンプルコード】
function sample() {
const numX = 5;
const numY = 10;
console.log(numX + numY);
}functionキーワードを冒頭に記述し、その後ろに関数名を続けて記述します。
そのため、上記の関数を実行したい場合は、「sample」 の名前で関数を呼び出します。
呼び出した際に実行されるのは {}ブロックの中に記述した処理なので、この場合は const numX … から console.log … までの行が実際に実行される処理となります。
上記の例を、今度は関数式で定義する場合で見てみましょう。
【サンプルコード】
const sample = function() {
const numX = 5;
const numY = 10;
console.log(numX + numY);
}こうして見比べてみると、構文としてはほとんど変わりがないのが分かりますね。
関数式の場合は変数名を使用して呼び出すので、この例の場合も sample の名前で呼び出すことができます。
ES2015で導入されたアロー関数式について
関数式で定義する場合は、ES2015より導入された 「アロー関数式」 という記述方法を用いることで、コードをより簡潔に記述することができます。
アロー関数式の基本構文は以下のとおりです。
【基本構文】
const sample = (引数1, 引数2) => {
//処理内容
}アロー関数式の特徴の1つとして、function キーワードの代わりに 「=>」 を使用して関数を定義します。
上記の例の場合は、通常の関数式と記述量は大きく変わりませんが、ケースによりここからさらに簡潔に記述することができます。
まず、引数が1つのみの場合は () を省略することができます。
【基本構文】
const sample = 引数1 => {
//処理内容
}また、処理内容のステートメントが1つのみの場合は、 {} を省略して記述することができます。
【基本構文】
const sample = () => 処理内容;関数で戻り値を指定する際は return文が必要となりますが (後ほど解説します)、{} を省略した場合は処理結果がそのまま戻り値となるため、return文の記述も不要となります。
この記述方法で、先ほどのサンプルコードを書き換えてみると、以下のような内容になります。
【サンプルコード】
const sample = () => {
const numX = 5;
const numY = 10;
console.log(numX + numY);
}定義した関数を呼び出す
関数の定義が完了したら、後は使用したいタイミングで呼び出しを行うだけとなります。
今度は、定義した関数を呼び出す方法について見ていきましょう。
基本的な関数の呼び出し
関数を呼び出す方法は簡単で、定義した関数名、もしくは関数を代入した変数名を使用したい場所に記述するだけです。
以下の例を見てみましょう。
【サンプルコード】
function sampleFunc() {
console.log("Hello, World!");
}
sampleFunc();【実行結果】
Hello, World!定義した 「sampleFunc」 の名前を記述することで、処理が実行されているのが分かりますね。
引数がない場合は、上記のように名前の後ろに () のみを記述し、引数がある場合は () の中に渡す値を指定します。
関数を呼び出す際は、定義方法によって記述場所に注意が必要です。
上述したように、ホイスティングの影響範囲がそれぞれ異なるため、ケースによりエラーになる場合があります。
例えば以下の例は、関数宣言の場合は問題なく関数を実行することができますが、関数式で定義した場合はエラーとなります。
【サンプルコード】
// 関数宣言の場合
sample1(); // 実行可能
function sample1() {
console.log("Hello, World!");
}
// 関数式の場合
sample2(); // エラー発生
const sample2 = function() {
console.log("Hello, World!");
}ページ内のイベントに応じて呼び出す
JavaScriptの場合は、ページの読み込みやボタンクリックなど、ページ上で発生したイベントに対して関数の呼び出しを行う場合も多々あります。
ページ内イベントに応じて呼び出す方法について、こちらも簡単な例をもとに見ていきましょう。
クリックしたときに呼び出す
特定のイベントに対して関数を実行させたい場合によく使用されるのが、addEventListnerです。
まずは、対象の要素をクリックした場合に関数を実行する簡単な例を紹介します。
【サンプルコード】
const btn = document.getElementById("btn");
btn.addEventListener("click", btnClickFunction);
function btnClickFunction() {
console.log('ボタンがクリックされました');
}addEventListnerで実行する関数を紐付ける場合は、まず初めに getElementByIdなどで対象の要素を取得します。
その要素に対して addEventListner を使用することで、イベントが発生した際に関数が実行されるようになります。
addEventListnerでは、様々な種類のイベントを引数に指定することができます。
今回は要素がクリックされた場合に関数を実行したいので、「click」 イベントを指定しています。
ページを読み込んだタイミングで呼び出す
ケースによっては、ページが読み込まれた際に何かしらの処理を実行したい場合もあるかと思います。
その際も、addEventListnerを使用して関数を実行させることができます。
【サンプルコード】
window.addEventListener("DOMContentLoaded", loadFunction);
function loadFunction() {
console.log('ページが読み込まれました');
}ページの読み込みに関するイベントに関数を紐付けたい場合は、windowに対してaddEventListenerを使用します。
指定できるイベントは主に2つあり、それぞれ以下のような違いがあります。
- DOMContentLoaded … DOMツリー構築完了時に呼び出し
- loaded … DOMツリー構築完了後、画像などの読込が全て完了した後に呼び出し
呼び出しタイミングとしては DOMContentLoadedが先となるため、画像などの読み込みを待つ必要がない場合は、DOMContentLoadedを使用する方が処理を速く済ませることができます。
関数に引数を設定する
関数は、引数を設定することで、ブロック内で使用する値を関数外から渡すことができます。
記述方法について見ていきましょう。
基本構文
まずは、関数宣言で引数を設定する方法について見ていきましょう。
【サンプルコード】
function sampleFunction(numX, numY) {
const result = numX + numY;
console.log(result);
}引数を設定する際は、()内に必要な数の分だけカンマで区切って、引数名を記述します。
設定した引数は、記述した名前で関数のブロック内で使用することができます。
関数式の場合も基本は同じですが、アロー関数で記述する場合は上述したように、引数の数によって書き方が異なります。
それぞれの書き方について以下の例で確認しましょう。
【サンプルコード】
// 基本構文
const sampleFunction = function(numX, numY) {
const result = numX + numY;
console.log(result);
}
// アロー関数、引数が1つの場合
const sampleFunction = numX => {
const result = numX + 10;
console.log(result);
}
// アロー関数、引数が2つ以上の場合
const sampleFunction = (numX, numY) => {
const result = numX + numY;
console.log(result);
}引数に値を渡す
関数に引数を設定した場合は、呼び出し時に実際に使用する値(実引数)を渡します。
以下の例で確認してみましょう。
【サンプルコード】
function sampleFunction(numX, numY) {
const result = numX + numY;
console.log(result);
}
sampleFunction(5, 10);【実行結果】
15上記の例では引数を2つ設定しているので、呼び出しの際に値を2つ、()内に記述して関数に渡しています。
JavaScriptは変数や引数で型を宣言することが無いため、関数に引数を渡す際は注意が必要です。
渡す値のデータ型よっては、想定しない動作をする場合があります。
例えば、先ほどのコードは本来、2つの数値を足してその結果を求める関数でしたが、数値以外の値を渡した場合に正確な結果を求めることができません。
【サンプルコード】
sampleFunction(‘Hello’, 10); // 数値ではなく文字列として処理される【実行結果】
Hello10必要に応じて、事前の型チェックなどを行うと、安全にコードを実行できるようになります。
【サンプルコード】
function sampleFunction(numX, numY) {
if(typeof numX === 'number' && typeof numY === 'number') {
const result = numX + numY;
console.log(result);
} else {
console.log('数値以外の値が渡されています');
}
}
sampleFunction(5, 10)
sampleFunction('Hello', 10);【実行結果】
15
数値以外の値が渡されています引数にコールバック関数を設定する
関数の引数には、別の関数を渡すこともできます。
以下の例のように、引数として親関数に渡し、特定のタイミングで実行される関数のことを、コールバック関数と呼びます。
【サンプルコード】
function sampleFunction(callback) {
console.log('コールバック関数を実行します');
callback();
}引数に渡すコールバック関数は、既に定義済みの関数を渡す方法と、その場で関数を定義して渡す方法の2通りで指定することができます。
既に定義済みの関数を渡す場合は、関数の呼び出し時に、コールバック関数として渡す関数の名前を () 内に記述します。
【サンプルコード】
sampleFunction(callFunc)注意点として、引数に渡すのは関数の実行結果ではなく関数そのもの (Functionオブジェクト) であるため、関数名の後ろに () は付けずに記述するようにしてください。
関数をその場で直接定義して渡す場合は、関数名の代わりに関数式を () の中に記述します。
【サンプルコード】
sampleFunction(function() {
console.log('コールバック関数を実行しました');
});コールバック関数は、主にAPIなどの非同期処理と組み合わせて使用されることが多い関数です。
非同期処理もまた、プログラムを実行する上で活用する機会は多々あるため、基本的な使い方と合わせて覚えておくようにしましょう。
関数で値を返す(戻り値を設定する)
関数は、引数を使って外から値を受け取るだけでなく、戻り値を設定してブロック内での処理結果を関数外に引き渡すこともできます。
続いては、関数で戻り値を設定する方法について見ていきましょう。
return文の書き方
関数で戻り値を設定する際は、return文を使用します。
基本的な書き方は、以下のとおりです。
【サンプルコード】
function sampleFunction(numX, numY) {
// 戻り値を return文で指定
return numX + numY;
}上記の場合は、引数に渡された2つの値を足した結果を、関数の戻り値として設定しています。
この関数を実行してみると、以下のような結果になります。
【サンプルコード】
const result = sampleFunction(10, 20);
console.log(result);【実行結果】
30このように、関数に戻り値を設定することで、関数内で処理した結果をブロックの外へと渡すことができます。
戻り値で条件分岐を行う
return文は、関数の中で複数記述されていても、エラーが発生することはありません。
ただし、いずれか1つの return文が初めに実行された段階で、関数の処理は終了しブロックの外へと処理が移ります。
【サンプルコード】
function sampleFunction() {
return console.log('1番目');
return console.log('2番目');
return console.log('3番目');
}
sampleFunction();
console.log('実行完了');【実行結果】
1番目
実行完了この性質を利用して、関数の戻り値を if文で条件分けすることができます。
【サンプルコード】
function sampleFunction(numX, numY) {
const result = numX + numY;
if (result > 10){
return true;
} else {
return false;
}
}
const result1 = sampleFunction(10, 20);
const result2 = sampleFunction(2, 3);
console.log(result1);
console.log(result2);【実行結果】
true
false関数の this キーワード
JavaScriptには、コードの実行環境 (実行コンテキスト) に応じた特定の値を参照することができる、「this」 というキーワードが存在します。
thisキーワードは関数内でも使用することができますが、その関数がどのように呼び出されたかによって参照できる値が異なります。
実際に thisキーワードがどの値を参照するかについて、呼び出し方法ごとに確認していきましょう。
通常の関数における this
単独の関数として呼び出された場合、その関数内で使用される thisキーワードは、グローバルオブジェクトを参照します。
グローバルオブジェクトは JavaScriptを実行している環境によって異なり、ブラウザの場合は Windowオブジェクトが対象となります。
【サンプルコード】
// 単独の関数として呼び出す
sampleFunc();
function sampleFunc() {
console.log(this); // グローバルオブジェクトを参照
}関数定義、もしくは関数式で定義した関数については同様の振る舞いをしますが、アロー関数で定義した場合は thisキーワードの参照する値が異なるため、注意が必要です。
(詳しい仕様については、後ほど解説します)
メソッドにおける this
オブジェクトのメソッドとして関数が呼び出された場合、thisキーワードはそのオブジェクト自身を参照します。
【サンプルコード】
class sampleClass {
constructor(name) {
this.name = name;
}
sampleFunc(){
console.log(this);
}
}
const myClass = new sampleClass('Taro Yamada');
myClass.sampleFunc();【実行結果】
Object { name: 'Taro Yamada' }コンストラクタ内でインスタンス変数を設定する際など、thisキーワードがよく使用されるケースの1つです。
アロー関数式における this
アロー関数式で定義した関数の中で thisキーワードを使用した場合は、関数宣言や関数式で定義した場合と挙動が異なります。
アロー関数式は、その他の方法で定義した関数とは違い、自分自身では thisキーワードを持ちません。(thisへのバインドがありません)
そのため、関数がどう呼び出されたかに関係なく、定義された時点での親スコープ(関数が定義された場所)の参照先を継承します。
まずは、以下の例を見てみましょう。
【サンプルコード】
function printName() {
console.log(this.name);
}
const sampleObj = {
name: 'Taro Yamada',
func: printName
}
printName();
sampleObj.func();【実行結果】
undefined
Taro Yamada関数宣言によって定義された関数の場合、単独で呼び出された時はグローバルオブジェクト、オブジェクトのメソッドとして呼び出された時はそのオブジェクトと、thisキーワードで参照される先が変化しています。
そのため、nameプロパティの値がそれぞれ異なっているのが、実行結果からも分かると思います。
一方で、printName関数の定義方法をアロー関数式に変更した場合の挙動はどうなるでしょうか。
実際の例で確認してみましょう。
【サンプルコード】
const printName = () => {
console.log(this.name);
}
const sampleObj = {
name: 'Taro Yamada',
func: printName
}
printName();
sampleObj.func();【実行結果】
undefined
undefined関数宣言で定義した時とは異なる結果が表示されましたね。
アロー関数式の場合は、上述したように、関数を定義した時点での親スコープの参照先を継承します。
上記のサンプルコードでは、定義時の親スコープの参照先がグローバルオブジェクトとなっているため、オブジェクトのメソッドとして呼び出した際も、対象のオブジェクトではなくグローバルオブジェクトを参照しています。
この性質を利用することで、通常の関数ではエラーになってしまうようなコード内容を、アロー関数を使って解決できる場合があります。
まずは、以下の例を見てみましょう。
【サンプルコード】
class sampleClass {
constructor(name) {
this.name = name;
}
showName() {
const func = function(){
console.log(this.name);
}
func();
}
}
const myClass = new sampleClass('Taro Yamada');
myClass.showName();【実行結果】
TypeError: Cannot read properties of undefined上記の結果の通り、このコードをそのまま実行するとエラーが発生してしまいます。
原因は、showName関数内で定義した関数をそのまま実行しようとしたことにあります。
JavaScriptには、strict modeという、グローバルオブジェクトを参照先とする thisへのアクセスを意図的に制限できる機能があり、クラス内ではこれが自動的に適用される仕組みとなっています。
メソッドでない関数の thisの参照先はグローバルオブジェクトとなるため、func関数内の thisのアクセスが制限され、エラーが発生しているのです。
func関数をアロー関数式を使用して定義すると、親スコープであるshowName関数の参照先を継承するため、sampleClassオブジェクトが thisの参照先となってこのエラーを解決することができます。
【サンプルコード】
showName(){
const func = () => {
console.log(this.name);
}
func();
}【実行結果】
Taro Yamada関数の「スコープ」とは?
先ほど、thisキーワードの解説に伴ってスコープについても軽く触れましたが、そもそもスコープとは何か?と疑問に感じた方もいたかもしれません。
スコープとは、コード上で宣言した変数や関数などを参照できる範囲のことを指します。
例えば、関数内で宣言した変数は基本的に、関数の外で呼び出すことはできません。
これは、変数の参照できる範囲がブロック内のみに限定されているからです。
このように、変数などがどの場所から参照できるかを定義したものを、スコープと呼びます。
JavaScriptにおけるスコープは、大まかに分けて以下の2つに分類されます。
- グローバルスコープ
- ローカルスコープ
グローバルスコープは、プログラム全体を参照範囲とするスコープです。
グローバルスコープ内で定義した変数はグローバル変数と呼ばれ、プログラムのどこからでも参照することができます。
一方、それ以外のスコープはローカルスコープに分類されます。
グローバルスコープと比べ、参照範囲がある程度限定されるのが特徴です。
ローカルスコープは、さらに以下の2種類に分類することができます。
- 関数スコープ
- ブロックスコープ
関数スコープは、その名の通りに関数内を参照範囲とするスコープです。
先ほど例に挙げた、関数内で宣言した変数のスコープも、この関数スコープが適用されています。
参照範囲が関数内のみとなるため、関数内で呼び出したり、引数に渡す値として使用することは可能ですが、宣言した関数の外で変数を使用することは不可能となります。
一方、ブロックスコープは、{} で囲われたブロックを参照範囲とするスコープとなります。
例えば、if文や for文などの {} 内で宣言した変数のスコープがこれに当たります。
こちらも関数スコープと同様に、ブロック内で宣言した変数を外から呼び出すことは不可能となります。
各スコープの参照範囲の大きさの順番は、グローバルスコープ > 関数スコープ > ブロックスコープ となっています。
これらの参照範囲を意図的に使い分けることで、変数の値を外から容易に変更できないようにするなど、コードの保守性を高めるために役立てることもできます。
まとめ
いかがでしたか?今回は、JavaScriptにおける関数の仕様や使用方法などについて解説しました。
書き方1つをとっても、様々な方法で定義できるのが、JavaScriptの関数の大きな特徴です。
関数を活用できる場面についても同様となります。
慣れない内は、書き方や使い方の違いを理解するのが大変かもしれませんが、プログラミングをする上で欠かせない重要な機能となりますので、しっかりと覚えて使いこなせるようにしていきましょう!
JavaScriptの勉強方法は?
書籍やインターネットで学習する方法があります。昨今では、YouTubeなどの動画サイトやエンジニアのコミュニティサイトなども充実していて多くの情報が手に入ります。
そして、より効率的に知識・スキルを習得するには、知識をつけながら実際に手を動かしてみるなど、インプットとアウトプットを繰り返していくことが重要です。特に独学の場合は、有識者に質問ができたりフィードバックをもらえるような環境があると、理解度が深まるでしょう。
ただ、JavaScriptに限らず、ITスキルを身につける際、どうしても課題にぶつかってしまうことはありますよね。特に独学だと、わからない部分をプロに質問できる機会を確保しにくく、モチベーションが続きにくいという側面があります。独学でモチベーションを維持する自信がない人にはプログラミングスクールという手もあります。費用は掛かりますが、その分スキルを身につけやすいです。しっかりと知識・スキルを習得して実践に活かしたいという人はプログラミングスクールがおすすめです。
プログラミングスクールならテックマニアがおすすめ!
ITスキル需要の高まりとともにプログラミングスクールも増えました。しかし、どのスクールに通うべきか迷ってしまう人もいるでしょう。そんな方にはテックマニアをおすすめします!これまで多くのITエンジニアを育成・輩出してきたテックマニアでもプログラミングスクールを開講しています。
<テックマニアの特徴>
・たしかな育成実績と親身な教育 ~セカンドキャリアを全力支援~
・講師が現役エンジニア ~“本当”の開発ノウハウを直に学べる~
・専属講師が学習を徹底サポート ~「わからない」を徹底解決~
・実務ベースでスキルを習得 ~実践的な凝縮カリキュラム~
このような特徴を持つテックマニアはITエンジニアのスタートラインとして最適です。
話を聞きたい・詳しく知りたいという方はこちらからお気軽にお問い合わせください。