30歳からのプログラミング

30歳無職から独学でプログラミングを開始した人間の記録。

アクセサプロパティ(getterとsetter)

アクセサプロパティの基本

プロパティには、データプロパティとアクセサプロパティの2種類がある。
必ずどちらかに分類でき、両方の性質を兼ね備えることは出来ない。

データプロパティには値が格納される。
アクセサプロパティは、値は持たず、gettersetterと呼ばれる関数を設定する。両方の関数を設定することも出来るし、片方だけ設定するということも可能。

以下の例では、_nameをデータプロパティとして、nameをアクセサプロパティとして、それぞれ設定している。

var person = {
    _name:'Tom',
    get name(){
        console.log('_nameを読み込みます');
        return this._name;
    },
    set name(value){
        console.log('_nameに'+value+'を書き込みます');
        this._name = value;
    }
};

console.log(person.name);
// _nameを読み込みます
// Tom

person.name = 'Bob';
// _nameにBobを書き込みます

console.log(person.name);
// _nameを読み込みます
// Bob

nameの値を読もうとするとgetterが呼ばれ、nameに値を代入しようとすると、setterが呼ばれる。
そのため、setterには必ず引数が必要。引数を取らない形でsetterを定義すると、エラーになる。
一方で、getterreturnを設定しなくても、エラーにはならない。その場合はundefinedが戻り値になる。

getterのないアクセサプロパティを読み込もうとすると、undefinedが返ってくる。
setterのないアクセサプロパティに書き込みをしようとすると、無視され、何も起こらない。strictモードでは、エラーとなる。

// getterがない
var person1 = {
    _name:'Tom',
    set name(value){
        console.log('_nameに'+value+'を書き込みます');
        this._name = value;
    }
};

console.log(person1.name);
// undefined

// setterがない
var person2 = {
    _name:'Tom',
    get name(){
        console.log('_nameを読み込みます');
        return this._name;
    }
};

console.log(person2.name);
// _nameを読み込みます
// Tom

person2.name = 'Bob';
// 何も起こらない strictモードだと、ここでエラーになる

console.log(person2.name);
// _nameを読み込みます
// Tom

プロパティの内部属性によるgettersetterの設定

データプロパティにはなく、アクセサプロパティだけが持つ内部属性に[[Get]][[Set]]がある。それぞれ、getter関数とsetter関数を格納する。
この属性を操作することによって、gettersetterを追加したり、内容を変更したりすることが出来る。
先ほどのコードの記法では、オブジェクト生成時にしか、gettersetterを定義できなかった。

プロパティの内部属性の操作を行えるdefineProperty()については、下記を参照。
プロパティの内部属性

// 生成した時点では、personはアクセサプロパティを持たない
var person = {
    _name:'Tom'
};

Object.defineProperty(person, 'name', {
    get: function(){
        console.log('_nameを読み込みます');
        return this._name;
    },
    configurable: true // この属性がtrueでないと、nameプロパティを後から操作することは出来ない
});

console.log(person.name);
// _nameを読み込みます
// Tom

// ここで、getterを上書きしつつ、setterを設定する
Object.defineProperty(person, 'name', {
    get: function(){
        console.log('これは、新しく設定されたgetterです');
        return this._name;
    },
    set: function(value){
        console.log('_nameに'+value+'を書き込みます');
        this._name = value;
    }
});

person.name = 'Bob';
// _nameにBobを書き込みます

console.log(person.name);
// これは、新しく設定されたgetterです
// Bob