これはJavaScriptの即時実行関数を使った
クロージャーのテクニックの一つです。
とはいえ、ES2015以降のコードではわざわざ利用する必要はありません。
昔のコードをリファクタリングする際に読めれば損はないよ程度のものです。
JavaScriptは関数が第一級オブジェクトです。
関数を変数に代入、関数の引数や戻り値として渡せる…という性質を持ってます。
またJavaScriptには関数を宣言した場合スコープを作ります。
中で宣言した変数・定数を束縛して外からはアクセス出来ないようにします。
この2つの性質を活用して、
JavaやPHPでのプライベート変数を作ってしまおうというのが
クロージャーの目的です。
普段我々は関数を定義する時にこうやりますよね。
js
1const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'];
2let colorIndex = -1;
3const getNextRainbowColor = function () {
4 if(++colorIndex >= colors.length) colorIndex = 0;
5 return colors[colorIndex];
6};
この時、colors
やcolorIndex
は外のスコープ内でむき出しになっており、
他の変数宣言等と衝突する可能性があります。
colorsなんてよくある名称ですからね。
そこで、一回関数を用意して包む事でスコープを作ります。
これでcolorsとcolorIndexを内部のスコープに閉じ込める事に成功しました。
js
1function hoge () {
2 const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'];
3 let colorIndex = -1;
4};
5
6hoge();
7console.log(colors);
8// Uncaught ReferenceError: colors is not defined
この関数を実行した時、未実行の無名関数を戻り値として返してあげます。
実行前の無名関数が戻り値だって!?
JavaScriptの関数は第一級オブジェクトなので、引数に設定したり戻り値として持ち運べるんでしたね。
戻り値の関数を受け取って実行すると
作った関数のスコープに潜り込んで動作が開始されます。
そして見事、関数内部で宣言したcolorsにアクセス出来ました。
js
1function constColors() {
2 const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'];
3 let colorIndex = -1;
4
5 return function () {
6 return colors;
7 }
8}
9
10const getColors = constColors();
11console.log(getColors()); // ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet']
道端の人全てに名前が振ってある小説とか読むの辛いですよね。
これはプログラムのコードでも同じことが言えます。
名前は必要最小限に振ってあげると可読性がよくなります。
何度も使う処理でないならば名前を削ってその場で実行してしまいましょう。
即時実行関数に加工して完成です。
js
1const getColors = (function () {
2 const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'];
3 let colorIndex = -1;
4
5 return function () {
6 return colors;
7 }
8})();
9
10console.log(getColors());
11// ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet']
おまけ
ES2015では正式にクラス構文が実装され、
クロージャーを使ったプロパティの隠蔽は非推奨です。
Javaにはオブジェクトのメンバー変数にプライベート属性を付けると、
外からそのメンバー変数を読み書きできなくなります。
同じ事をJavaScriptでもクロージャーを使って実現しようという発想は多くあり、
昔はこのようなコードを好んで書くエンジニアも居ました。
古い技術ブログやQiitaの記事を漁れば出てくるかもしれませんね。
これはオブジェクト指向言語のプライベート変数を再現したコードになります。
戻り値にオブジェクトを作ってメソッドのようにしているのがわかります。
js
1const newUser = (function () {
2 var name = "";
3 var age = 0;
4
5 return function (n, a) {
6 name = n;
7 age = a;
8 return {
9 getName: function(){ return name; },
10 getAge: function(){ return age; }
11 }
12 }
13})();
14
15const user = newUser("taro", 18);
16console.log(user.getName()); // "taro"
17
18console.log(user.name); // undefined
しかしこの方法はgetNameやgetAge等という関数を大量に生成するので、
APIのJSONを解析してuserを1000件生成しましょうとなるとメモリの消費量がヤバイことになります。
なので、メモリ消費量を気にするならば、素直にprototype変数を使ったほうがよくて、
それを綺麗な形で包んでくれるクラス構文を使ったほうが良いのです。
JavaScriptはこのオレオレクラス構文がプロジェクト毎に存在しており、時にはバグの温床になったりする程でしたからね。
プライベート変数が存在しない問題も残っていますが、
同じくプライベート変数が存在しないPythonでは
もうプロパティの先頭文字を_
にしてプログラミングする際は_
で始まるプロパティを参照しないルールで実装すればよくね?
みたいな慣習なようで、JavaScriptも同様のスタイルのプロジェクトをよく見かけます。