JavaScript本格入門(ISBN 978-4774184111)で基礎からJavaScriptを勉強するシリーズです。
今回はChapter5からアクセサーメソッドについてです。
アクセサーメソッドは、セッターメソッドとゲッターメソッドを合わせた言い方です。
アクセサーメソッドの実装の仕方と使用方法について勉強したのでまとめます。
アクセサーメソッドを使わない実装
let human = {
name:"",
age:"",
}
// 使う側(値のセット)
human.name = "Giorno Giovanna";
human.age = 15;
// 使う側(値のゲット)
console.log(human.name); // Giorno Giovanna
console.log(human.age); // 15
非常にシンプルな実装です。
続いてアクセサーメソッドを用いた実装をしてみます。
アクセサーメソッドを使った実装
let human = {
set name(value){
this._name = value;
},
get name(){
return this._name;
},
set age(value){
this._age = value;
},
get age(){
return this._age;
}
}
// 使う側(値のセット)
human.name = "Giorno Giovanna";
human.age = 15;
// 使う側(値のゲット)
console.log(human.name); // Giorno Giovanna
console.log(human.age); // 15
humanオブジェクトの中にset構文とget構文を用いてプロパティを定義してみました。
まずセッターメソッドの動きを見てみます。
-
human.name
に値をセットすると、セッターメソッドset name(value)
が呼ばれます - セッターメソッドの中では、
human._name
というプロパティを新しく作成し、そこにvalue
の値を格納します
使う側は、human.name
に値を代入したつもりなのですが、セッターメソッドの動きによって値をhuman._name
という別のところに格納したことになります。
次にゲッターメソッドの動きを見てみます。
-
human.name
にアクセスしようとすると、ゲッターメソッドget name()
が呼ばれます - ゲッターメソッドの中では、
human._name
というプロパティの値を戻り値として返します
使う側は、human.name
の値を取得したつもりなのですが、ゲッターメソッドの動きによってhuman._name
という別の値を取得させられます。
セッターメソッドとゲッターメソッドの働きによって、間接的にhuman._name
の値をやり取りするので、使う側からはhumanオブジェクトの中身がアクセサーメソッドを使って実装されてあろうがなかろうが、値のセットとゲットの方法は変わらないことに注目です。
値のセットとゲットにワンクッションあるだけですので、これではあまり何がうれしいのかわかりませんので、セッターとゲッターを使うと便利になる例を紹介します。
例1: プロパティに格納する値のチェックをしたい場合
human.name
に入る値は文字列でないといけないし、human.age
は数値であるべきです。
そのような場合、セッターメソッドにチェックする実装を入れてやることができます。
let human = {
name:"",
age:"",
}
// 使う側(値のセット)
human.name = 15; // 望ましくない値として数値が入ってきた
human.age = "Giorno Giovanna"; // 望ましくない値として文字列が入ってきた
// 使う側(値のゲット)
console.log(human.name); // 15
console.log(human.age); // Giorno Giovanna
望ましくない値がそのままプロパティにセットされてしまいます。
let human = {
set name(value){
if(typeof value === "string"){
this._name = value;
}else{
console.log("nameが文字列じゃないじゃあないか!");
}
},
get name(){
return this._name;
},
set age(value){
if(typeof value === "number"){
this._age = value;
}else{
console.log("ageが数値じゃないじゃあないか!");
}
},
get age(){
return this._age;
}
}
// 使う側(値のセット)
human.name = 15;
human.age = "Giorno Giovanna";
// 使う側(値のゲット)
console.log(human.name); // undefined
console.log(human.age); // undefined
nameが文字列じゃないじゃあないか!
ageが数値じゃないじゃあないか!
undefined
undefined
セッターメソッドで値のチェックをすることができ、使う側が幾分か安全になりました。
例2: プロパティを読み取り専用にしたい場合
年齢は毎年変わりますが、名前はそう簡単には変わるものではありません。
なので、human.age
は後から書き換え可能にしたいですが、human.name
は後から変えられないように読み取り専用にしたいとします。
let human = {
name: "Giorno Giovanna", // もともとの名前
age: 15, // もともとの年齢
}
// 使う側(値のゲット)
console.log(human.name); // Giorno Giovanna
console.log(human.age); // 15
// 使う側(値のセット)
human.name = "汐華初流乃"; // 改名!
human.age = 16; // 加齢!
// 使う側(値のゲット)
console.log(human.name); // 汐華初流乃(改名されてしまった。望ましくない)
console.log(human.age); // 加齢された(これは望ましい)
これではhuman.name
の値が後から書き換えられてしまいます。
アクセサーメソッドを使ってhuman.name
を読み取り専用にしてみます。
let human = {
_name : "Giorno Giovanna", // もともとの名前
_age : 15, // もともとの年齢
get name(){
return this._name;
},
set age(value){
this._age = value;
},
get age(){
return this._age;
}
}
// 使う側(値のゲット)
console.log(human.name); // Giorno Giovanna
console.log(human.age); // 15
// 使う側(値のセット)
human.name = "汐華初流乃"; // 改名!
human.age = 16; // 加齢!
// 使う側(値のゲット)
console.log(human.name); // Giorno Giovanna(改名が阻止できた)
console.log(human.age); // 加齢された(これは望ましい)
改名が阻止できました。
何をしたかというと、nameのセッターメソッドの実装をやめゲッターメソッドだけ持つようにするようにしました。これにより、human.name
は読み取り専用となりました。
human.age
は後で加齢できるように読み書きできるようにセッターメソッド、ゲッターメソッドの両方を持ちます。
例3: 例1と2を組み合わせる
プロパティに入れられる値をチェックし、なおかつ読み書きの制御も行います。
human.name
は後から書き換えできないように、human.age
はプロパティの値を加算するための専用の関数を定義します。これにより不用意に年齢を減らされたりすることがなくなるという意図です。
let human = {
_name : "Giorno Giovanna", // もともとの名前
_age : 15, // もともとの年齢
get name(){
return this._name;
},
get age(){
return this._age;
},
addAge(){ // 年齢を加算するための関数
human._age++;
}
}
console.log(human.name); // Giorno Giovanna
console.log(human.age); // 15
// 使う側(値のセット)
human.name = "汐華初流乃"; // 改名!
human.addAge(); // 加齢!
// 使う側(値のゲット)
console.log(human.name); // Giorno Giovanna (セッターメソッドを持たないので改名は失敗)
console.log(human.age); // 16 (addAgeを使って加齢できている)
まとめ
セッターメソッドとゲッターメソッドを使うことで、
- プロパティに予期しない値が入るのを防ぐこと
- プロパティの値が後から書き換わることを防ぐこと
ができることがわかりました。
ただし、この実装では限界があり、使う側が直接human._name
やhuman._age
をアクセスしてしまうと、1、2が保証できなくなってしまいます。
コーディング規約などでアンダースコア付きのプロパティには直接アクセスしないことをお約束しておく必要があります。
おまけ
クロージャやシンボルを使うともう少し安全なアクセサーメソッドを実装することができます。
以下の記事で紹介されているものがわかりやすいです。
JavaScriptのgetter/setterの使い方を考えよう