クロージャは、独立した (自由な) 変数を参照する関数です。言い換えるとクロージャ内で定義された関数は、自身が作成された環境を '覚えています'。
なんだか意味深ですね。
わからなくて意味の深そうなものは全部意味深って言ってます。
早速だからクロージャをみてみます。
function closure () {
var value = 0;
return function next(){
value++;
console.log(value);
};
};
let n = closure();
n(); //1
n(); //2
n(); //3
戻り値がfunction
になってます!
そのnext()
の中でvalue
を参照していますね。
n
にはnext()
が入っているので、呼び出すたびにvalueがプラス1されます。
それでクロージャってなんに使うの?
先ほどの例では、ファンクションであるにも関わらず、前回のvalueの値を元に
どんどん加算することができています。
まるでオブジェクトみたいな使用ができるんですね。
そしてクロージャ役に立つ方法を知る為に、、まずプライベートメンバについてまとめます。
プライベートメンバーって?
プライベートメンバー
・・・クラス内部からのみ呼び出せるプロパティ/メソッドのこと。
パブリックメンバー
・・・クラス内外から自由にアクセスできるメンバーのこと。
javascriptでは普通に定義するとパブリックメンバーになる。
→先ほどのクロージャを使うことで擬似的にプライベートメンバーを定義できる!
var プロパティ名
var メソッド名 = function(引数,...){}
これをコンストラクター関数の中で定義することで、プライベートメンバーにできます!
this.ほにゃらら= って書かないんですね。
プライベートメンバーにアクセスする方法
特権メソッド(プリビレッジメソッド)・・・プライベートメンバーにアクセスできるメソッド。
こいつらはコンストラクタ内で関数として定義する。⇨クロージャ!
function Neko(){
var _voice = "mew"; //プライベートプロパティ
var _intro = function(){ //プライベートメソッド
console.log("猫です");
};
this.getVoice = function(){ //特権メソッド
return _voice
};
this.getIntro = function(){
_intro();
}
}
var neko = new Neko();
console.log(neko.getVoice()); //mew
neko.getIntro(); //猫です
もう1つ、プロパティ自体にアクセスさせない定義方法をみてみます。
function Neko(){
var _voice; //プライベートプロパティsetVo
this.setVoice = function(voice){
_voice = voice;
}
this.getVoice = function(){
console.log(`${_voice}と鳴いた`);
}
}
var neko = new Neko();
neko.setVoice('ミャー');
neko.getVoice();
プロパティを直接外部から操作せず、プロパティにアクセスする為のメソッドを準備しています。
こんなメソッドをアクセサーメソッドと言います。
また参照用メソッド(ここでいうgetVoice)をゲッターメソッド,
設定用メソッド(ここでいうsetVoice)をセッターメソッドと細かく呼ぶこともあります。
アクセサーメソッド
アクセサーメソッドなんて面倒くさいことしやがって!と思いましたが、
たくさんのメリットがあるようです。
①プロパティを書き込み専用にできる!
→ゲッターメソッドだけを用意すればいい!
②プロパティを読み取り専用にできる!
→セッターメソッドだけを用意すればいい!
③値の参照時にデータを加工できる!
→ミャーと鳴いた、というのがそうですね。他にも余計なデータをとったり付け足してわかりやすくしたり、、
④値の設定時に妥当性のチェックができる!
→おかしなデータを代入前に弾くことができますね。
でももっと簡単にアクセサーメソッドを実装する方法があるみたいです。
今までの方法はレガシー向けで使用していいみたいです。
definePropertyメソッド
Object.defineProperty(obj,prop,desc)
//obj:プロパティの定義先のオブジェクト
//prop:プロパティの名前
//desc:プロパティの構成情報
desc
に対してget/setパラメータを指定すると、ゲッター/セッターを定義できます。
function Neko(){
var _voice;
Object.defineProperty(
this,'voice',
{
get:function(){
return console.log(`${_voice}と鳴いた`);
},
set:function(voice){
_voice = voice;
}
}
)
}
var neko = new Neko();
neko.voice = 'にゃー';
neko.voice; //にゃーと鳴いた
なきました!