クロージャによるプロパティの隠蔽
オブジェクトのプロパティ
例として、以下のようにオブジェクトリテラルを使ってオブジェクトを生成したとする。
var quo1 = { status: 1, set_status: function(s) { this.status = s; }, get_status: function() { return this.status; } };
nodeで実行。
> quo1.get_status(); 1 > quo1.status 1
statusプロパティはパブリックな状態のため、メソッドを介さずとも直接参照することができる。
そこでメソッドのみをプロトタイプで定義し、コンストラクタで生成する。
var Quo = function() { this.status = 2; }; Quo.prototype.set_status = function(s) { this.status = s; }; Quo.prototype.get_status = function() { return this.status; }; var quo2 = new Quo();
nodeで実行。結果は同じ。statusプロパティは前者と同じくパブリックな状態のため外部からアクセス可能。
> quo2.get_status(); 2 > quo2.status 2
statusプロパティもプロトタイプで定義して、コンストラクタで生成する。
var QuoEx = function() {}; QuoEx.prototype.status = 3; QuoEx.prototype.set_status = function(s) { this.status = s; }; QuoEx.prototype.get_status = function() { return this.status; }; var quo3 = new QuoEx();
nodeで実行。statusプロパティへのアクセスがプロトタイプチェーンによるものに変わったが、やはり外部からアクセス可能。
> quo3.get_status(); 3 > quo3.status 3
どうにかしてプロパティを隠蔽できないかと考える。そこでクロージャ。
クロージャとは何か?
サイ本では以下のように説明されている。
JavaScriptの関数は、実行するコードと、コードを実行するスコープを組み合わせたものです。コードとスコープが対になったものを、コンピュータサイセンスの分野ではクロージャ(closure)と呼んでいます。しかし、JavaScriptの関数がクロージャとして面白い動きをするのは、前述したように、入れ子型の関数が、定義されたスコープ以外で使われる場合だけです。実際に、JavaScriptでクロージャという言葉を使うのは、入れ子型の関数をこのような使い方をする場合のみです。
要は、
1. JavaScriptでいうクロージャとは、入れ子型の関数とそのスコープ(を指すのが一般的)
2. 入れ子型の関数(の実行コンテキスト)からは入れ子元の関数で定義された変数が参照できるが、グローバル(の実行コンテキスト)からは入れ子元の関数で定義された変数を参照することはできない。
3. これをうまく使うと、入れ子型の関数からしかアクセスできないプライベートなプロパティを実現することができる
2.は、Callオブジェクトによるスコープチェーンの性質そのもの。
クロージャで実装するプライベートなプロパティ
というわけで実装してみる。
var quo = function() { var that = {}; var status = 4; that.get_status = function() { return status; }; that.set_status = function(s) { status = s; }; return that; }; var quo4 = quo();
nodeで実行。
> quo4.get_status(); 4 > quo4.status undefined
成功。statusプロパティを外部から直接参照することができない。
まとめ
* クロージャとは実行コードとスコープがセットになったものを指すが、JavaScriptに限って言えば、入れ子型の関数が外部から呼ばれた場合、自己のスコープだけでなく入れ子元のスコープを参照すること
* その性質を使うと、ひとつの例としてプロパティを隠蔽することができる