【JavaScript基礎】関数定義の基礎、サーキット演算、三項演算子について
<目次>
関数定義の方法
①function命令で定義関数
もっとも基本的な関数定義の方法。
functionで始めることでこれから関数を定義するということを宣言する。
// 関数myFuncを定義 function myFunc() { // ここに処理を書く } // 関数実行 myFunc();
②関数リテラルで定義
関数はデータ型の一種のため、変数に入れることができる。この特性を利用した関数定義方法。
(そのほかにも関数を引数や戻り値として設定することができる。)
// 関数リテラルを変数myFuncに代入 var myFunc() = function() { // ここに処理を書く } // 関数実行 myFunc();
上記のように関数リテラルは宣言時には名前を持たないため無名関数あるいは匿名関数と呼ばれる。
③Functionコンストラクタ経由で定義
基本使うことはない。(理由は後述)
組み込みオブジェクト(標準で組み込まれている、JavaScriptが動作するすべての環境で使用できるオブジェクト)であるFunctionオブジェクトのコンストラクタ(オブジェクトを初期化するためのオブジェクトと同名のメソッド)を利用して定義する方法。
Functionコンストラクタに、関数が受け取る仮引数と、関数本体を指定するのが基本的な使い方。
// 関数オブジェクトを変数myFuncに代入 var myFunc = new Function('引数1', '引数2', '//ここに処理を書く'); // 関数実行 myFunc();
「文字列として引数や関数本体を定義できる」という特徴があるため下記のような記述も可能。
var param = 'num1, num2'; // 引数 var func = 'return num1 + num2'; // 関数本体 var sum = new Function(param, func); // 関数定義 console.log(sum(4,6)); // 10
上記の変数param、funcは固定値を代入しているがスクリプトで動的に生成することもできる。
しかし第三者による任意のスクリプトが実行できてしまうリスクがあったり、通常のコードよりも処理速度が遅いため使用しない方が無難。
また、①と②の定義方法と比べて可読性が劣るので、
基本的に、関数定義は①function命令、②関数リテラルを使用することが推奨されている。
①function命令での定義と、②関数リテラルでの定義の関数の使用上の注意
関数登録のタイミングの違い
①のfunction命令での関数定義では、コードを解析/コンパイルするタイミングで関数を登録しているので、どこからでも呼び出せる。
(変数の宣言と同じようにプログラムの先頭への巻き上げ[ホイスティング]が行われる)。
// 先に関数実行のコードを記述しても問題なし // 関数実行 console.log(sum(4, 6)); // 10 // 関数定義 function sum (num1, num2) { return num1 + num2; }
対して②の関数リテラルでの関数定義では、無名関数を変数に代入しているということから、変数に代入したあとでないと実行できない。
つまり関数実行のコードよりも先に関数定義しなくてはならない。
(③のFunctionコンストラクタ経由での定義も同様)
// 先に関数実行のコードを記述するとエラーになる // 関数実行 console.log(sum(4, 6)); // エラー // 関数定義 var sum = function(num1, num2) { return num1 + num2; }
即時関数について
即時関数とは
無名関数を定義すると同時に実行するための構文。
使わずに通常の名前付き関数で代用できるが、スマートに記述が可能になる。
書き方
実行するための()の位置が違うだけで、下記のどちらでもOK。
// 書き方① (function() { // ここに処理を書く })(); // 書き方② (function() { // ここに処理を書く }());
関数名を付けて、関数内でのみ再度呼び出すこともできる(再帰という)。
// 即時関数にcounterという関数名をつける (function counter(cnt) { console.log(cnt); setTimeout(function(){ if (cnt >= 100) { return; }; cnt++; counter(cnt); // 再帰 }, 1000); }(1));
即時関数のメリット
関数名を書かなくても良いので、1回しか使わない関数をスマートに記述できる
// 即時関数を使わない場合 function sum(num1, num2) { return num1 + num2; } var result = sum(4, 6); console.log(result); // 即時関数を使った場合 var result = (function(num1, num2) { return num1 + num2; }(4, 6)); console.log(result);
基本的に、複数使いまわす関数は名前付き関数にして、一度しか使わない関数は即時関数にすることで、関数名を考える余計な手間が省ける。
またコードの記述量が減る。
スコープの汚染を防ぐ
関数内で宣言された変数は関数内にスコープを持つため、関数の外からはアクセスできない。
この性質を利用することで関数を名前空間として使うことができる。
var x = 'Global x'; (function() { var x = 'Local x'; var y = 'Local y'; }()); console.log(x); // 'Global x' console.log(y); // エラー
上記において、即時関数内で宣言された変数はこの関数内でのみ有効な変数のため、グローバル変数の名前と衝突しない。
このことから、ライブラリを読み込んで利用する場合、ライブラリのグローバル変数との衝突を避けたり、モジュールを作成する際に他モジュールの変数との衝突を避けることができる。
即時関数が()で括られている意味
JavaScriptではfunction宣言の前に文字があると、関数が式として評価され関数オブジェクトへの参照値となり、直後に()をつけて実行できるようになる。
(逆にfunctionの前に文字がなければfunction命令の文として評価されて、直後に()がつけられない)。
詳しくは下記を参照。
サーキット演算
サーキット演算とは
if文などの条件分岐で使われる&&や||などの論理演算子の特性を利用した演算のこと。
ショートカット演算や短絡演算とも呼ばれる。
書き方
// ショートカット演算を使わない場合 var x = 1; if (x === 1) { console.log('Hello.'); } // ショートカット演算を使った場合 var x = 1; x === 1 && console.log('Hello.');
A && Bでは、「AがtrueかつBもtrueだったら」という意味。
AがもしfalseだったらBがtrueでも全体としてはfalseとなるため、Bの判定はしても意味がない。
そのためAがfalseであったらBの判定は行われない。つまりAがtrueの場合のみ、Bが実行される。
A || Bでは、「AがtrueあるいはBがtrueだったら」という意味。
Aがもしtrueであったらその時点で全体としてはtrueとなるため、Bの判定はしても意味がない。
そのためAがtrueであったらBの判定は行われない。つまりAがfalseの場合のみ、Bが実行される。
使用に関して
サーキット演算を用いればコードを短く書くことができるが、条件分岐であることがぱっと見でわかりにくいので基本的には使用しない方が無難。 ただ他者のコードなどで見かけた場合はこの処理を思い出して意味が分かることが重要かと。
詳しくは下記参照。
三項演算子
三項演算子とは
その名の通り、三項をもつ演算子でif文のショートカットとして使われる。
条件演算子とも呼ばれる。
書き方
// 三項演算子を使わない場合 var score = 90; if (score >= 80) { console.log('合格'); } else { console.log('不合格'); } // 三項演算子を使った場合 var score = 90; score >= 80 ? console.log('合格!') : console.log('不合格…');
「条件式 ? trueだった場合の処理 : falseだった場合の処理」という書き方。
他の言語でも使わることが多く、スマートな記述が可能になる。
入れ子での記述も可能だが、あまり見かけたことはない。
詳しくは下記参照。