こんにちは、ほそ道です。
今回はJavaScriptのGetterとSetterについてやって行きます。
Getter
Getterの仕様
それでは早速、Getterの定義から。
var man = {
firstName: '',
lastName: '',
get fullName() {
return this.firstName + ' ' + this.lastName;
},
};
get
というキーワードを使用します。
下記の様な感じで使用できます。
man.firstName = 'Bill';
man.lastName = 'Gates';
console.log(man.fullName); // Bill Gates
まあ予想通りの動きになります。
次にfullNameプロパティに直接アクセスしてみましょう
man.firstName = 'Bill';
man.lastName = 'Gates';
man.fullName = 'Steve Jobs';
console.log(man.fullName); // Bill Gates
下から二行目のman.fullNameへの代入は華麗にスルーされました。
ちなみにuse strict
してstrictモード上で実行するとman.fullName = 'Steve Jobs';
とアクセスした時にTypeErrorが発生して怒られます。
fullNameを直接セットする事は出来くなります。
TypeError: Cannot set property fullName of #<Object> which has only a getter
Setter
Setterの仕様
続いてSetterもやっていきましょう。
var man = {
_age: 0,
get age() {
return this._age;
},
set age(val) {
console.log('[debug]age=' + val);
this._age = val;
}
};
今度はset
のキーワードを使用します。
また、Setter宣言時には必ずしもGetterを宣言する必要は無いのですが(strictモードでも動く)、お作法的にはGetterも宣言するべきなようで、しておかないとJsLintからは注意されました。
Setterの注意点
Setterはなにがしか別名の変数に値を保存する必要があります。
例えば
var man = {
get age() {
return this.age;
},
set age(val) {
this.age = val;
}
};
上記のようにすると代入時に再帰的にSetterがコールされて無限ループのようになりスタックサイズ超過が発生してしまいます。
Getter/Setterのメリット
この仕組みがどんなときに効力を発揮するのか考えてみました。
-
データのカプセル化をコンストラクタ宣言せずに、オブジェクトリテラルで行う事ができる
-
データゲット時にイベント的にログを吐く等追加処理が行える
カプセル化
下記のように関数でくるんでデータ保存先の変数を隠蔽すると、カプセル化できます。
var man = (function() {
var _age = 0;
return {
get age() {
return _age;
},
set age(val) {
_age = val;
}
}
}());
あと、下記のようにすると一見、うまいことSetter/Getter内でageという変数が使われているように見えなくもないですがグローバル空間にage変数が宣言されてしまっているので気を付けましょう。
var man = {
get age() {
return age;
},
set age(val) {
age = val;
}
};
man.age = 10
console.log(age); // 10
アンチパターン
Getter/Setterに関して、追加処理を行う事が出来ますが、この場合にやらない方が良さそうな事を挙げておきます。
×:Setterで引数の値を書き換える
-> まず利用者からは状況がつかめず想定外が発生する可能性が高まります。
×:Setterで値チェックを行い、不正な場合にthrow new Error
する
-> 結果的に代入文をtry〜catchすることになりかなり不自然になります。エラーを発生させるならsetAgeなどの関数にした方が良いでしょう。
オブジェクト定義後にGetter/Setterを追加する
最後に、ここまでset/getキーワードを使用したやり方を紹介してきましたが
このやり方はオブジェクト定義時にSetter/Getterを併せて定義する必要があります。
オブジェクト定義後にGetter/Setterを追加する事も出来るのでこちらも見ておきます。
var man = {
_age: 0
};
man.__defineGetter__('age', function() {
return this._age;
});
man.__defineSetter__('age', function(val) {
this._age = val;
});
__defineGetter__
、__defineSetter__
という関数を使います。
※非標準なメソッドでIEでは搭載されていません
ただし、このやり方だと内部でデータを持っている_ageをカプセル化出来ないように思います。
ココがずっと悩ましいなーと引っかかっているので上手く出来たら追記します。
今回は以上です。