本文章ではJavaScriptのGetterとSetter ,Object.defineProtperyについて説明を行っています。ライブラリのコードを読むときや文書を読む時に目にする機会があるので自分で利用する機会がなかったとしても知っていて損はありません。

ネット上でGetterとSetterの例に利用されているirstNameとlastNameとfullNameの説明を使っています。

フルネームを表示する方法

まずfirstNameとlastNameをもつuser オブジェクトを定義します。


let user = {
  firstName: "John",
  lastName: "Doe",
};

このユーザのフルネームを表示させたい場合はどうしますか?

変数の結合

1 つ目の方法としては以下のように記述することでフルネームを表示することはできます。


console.log(user.firstName + " " + user.lastName);
console.log(`${user.firstName} ${user.lastName}`); // ES6 今後はこちらを利用
//結果
John Doe
John Doe

しかしこの場合はフルネームを表示させたい場合にこの長いコードを毎回記述する必要があります。

メソッドを利用した方法

つぎに考えられるのが、オブジェクトにfullNameメソッドを追加することです。追加したメソッドを実行したい場合はuser.fullNameのカッコをつける必要があります。


let user = {
  firstName: "John",
  lastName: "Doe",
  fullName: function () {
    return `${user.firstName} ${user.lastName}`;
  },
};

console.log(user.fullName());
//結果
John Doe
console.log(user.fullName()); //結果 John Doe

Getterを利用した方法

Getterを利用すると以下のように記述することができます。


let user = {
  firstName: "John",
  lastName: "Doe",

  get fullName() {
    return `${user.firstName} ${user.lastName}`;
  },
};

console.log(user.fullName);
//結果
John Doe

メソッドの場合と異なり、実行する際はカッコが必要でなくfirstNameやlastNameのプロパティにアクセスする場合と同じように記述を行うことができます。

このようにGetterを利用することでfirstNameとlastNameを結合し、プロパティのように扱えることがわかりました。

.(ドット)ではなく[]カッコを利用してもアクセスすることは可能です。


console.log(user["fullName"]);
//結果
John Doe

通常のプロパティのようにアクセスできたので値も設定できるのではと考えてしまいます。しかしGetterだけではfullNameに対して値を設定したも設定を反映することができません。


user.fullName = "Kit Harington";

値を設定するために利用できるのがSetterです。

Setterを利用した値の設定

Setterを利用して値を設定する前に空白を挟んで設定される名前の処理方法について確認を行っておきます。これは今回の例で利用する場合に知っておかなければならないメソッドなので直接Setterに影響を与えるものではありません。

splitメソッドの理解

John Doeというフルネームが与えられたら真ん中に入っている空白によってfirstNameとlastNameをわける必要があります。splitに区切り文字である空白を入れることでfirstNameとlastNameにわけることができます。


fullName = "John Doe";
console.log(fullName.split(" "));
//結果
[ 'John', 'Doe' ]

分割代入を利用すると下記のようにsplitで分けたfirstNameとlastNameを取得することができます。


fullName = "John Doe";
[firstName, lastName] = fullName.split(" ");
console.log(firstName);
console.log(lastName);

Setterを利用した方法

setではfullNameに引数valueを設定し、splitメソッドで文字列を空白で分割し、分割代入でfirstNameとlastNameを設定しています。この時thisをつける必要があります。


let user = {
  firstName: "John",
  lastName: "Doe",

  get fullName() {
    return `${user.firstName} ${user.lastName}`;
  },
  set fullName(value) {
    [this.firstName, this.lastName] = value.split(" ");
  },
};

user.fullName = "Kit Harington";

console.log(user.fullName);

// 結果
Kit Harington

getとsetで同じfullNameを利用していますが、”=”(イコール)で値を設定する場合はsetterが利用され、ただ値を取得する場合はgetterが利用されます。

setterの中で入力した文字列のチェックを行うことも可能です。matchメソッドを利用して入力した文字列に空白がないかチェックをして空白がある場合はメッセージを出力します。


let user = {
  firstName: "John",
  lastName: "Doe",

  get fullName() {
    return `${user.firstName} ${user.lastName}`;
  },
  set fullName(value) {
    if (!value.match(" ")) {
      console.log("入力した文字の名前に空白がありません。");
      return;
    }
    [this.firstName, this.lastName] = value.split(" ");
  },
};

user.fullName = "KitHarington";

console.log(user.fullName);

// 結果
入力した文字の名前に空白がありません。
John Doe

 文字列を空白を入れた場合はメッセージは出ません。

 Object.defineProtperyメソッド

 Object.defineProtperyを利用することでGetterとSetterを設定することができます。


let user = {
  firstName: "John",
  lastName: "Doe",
};

Object.defineProperty(user, "fullName", {
  get: function () {
    return `${user.firstName} ${user.lastName}`;
  },
  set: function (value) {
    [this.firstName, this.lastName] = value.split(" ");
  },
});

user.fullName = "Kit Harington";

console.log(user.fullName);

Object.definePropertyの書式は下記の通りです。

Object.defineProperty(オブジェクト, プロパティ名, ディスクリプター)

上記の例ではオブジェクトにはuserオブジェクトが対応し、プロパティ名はアクセスする際に利用するプロパティ名のfullNameが対応し、ディスクリプターには、getとsetを持つオブジェクトが対応しています。

enumerableの設定

Object.definePropertyにはget, set以外にもconfigurable, enumerableを設定することができます。enumerableについてtrue, falseを設定することができます。true, falseに設定することによりどのような違いがあるか確認します。

デフォルトではenumerableはfalseですが、明示的に指定すると以下のように設定することができます。


Object.defineProperty(user, "fullName", {
  get: function () {
    return `${user.firstName} ${user.lastName}`;
  },
  set: function (value) {
    if (!value.match(" ")) {
      console.log("入力した文字の名前に空白がありません。");
      return;
    }
    [this.firstName, this.lastName] = value.split(" ");
  },
  enumerable: true,
});

forループを利用するとObject.definePropertyで追加したfullNameは表示されます。


for (key in user) {
  console.log(key);
}
//結果
{ firstName: 'Kit', lastName: 'Harington', fullName: 'Kit Harington' }

enumerableをfalseに設定するとfullNameは表示されません。


// enumerableがfalseの場合
for (key in user) {
  console.log(key);
}
//結果
{ firstName: 'Kit', lastName: 'Harington' }

Object.assignの場合

Object.assignを利用してオブジェクトをマージする際にenumerableがtrueの場合はfullNameはマージされますがfalseの場合はマージに含まれません。


// enumerableがtrueの場合
console.log(Object.assign({}, user));
//結果
{ firstName: 'Kit', lastName: 'Harington', fullName: 'Kit Harington' }

// enumerableがfalseの場合
console.log(Object.assign({}, user));
//結果
{ firstName: 'Kit', lastName: 'Harington' }

configurableの設定

configurableを設定することでdeleteを利用したプロパティの削除を制御することができます。設定する場合はenumerableと同様に以下のように設定を行います。デフォルトではfalseに設定されています。


Object.defineProperty(user, "fullName", {
  get: function () {
    return `${user.firstName} ${user.lastName}`;
  },
  set: function (value) {
    if (!value.match(" ")) {
      console.log("入力した文字の名前に空白がありません。");
      return;
    }
    [this.firstName, this.lastName] = value.split(" ");
  },
  enumerable: true,
  configurable: true,
});

trueの場合は削除することができるのでdeleteした後はundefinedになります。falseの場合は削除できないためfullNameがそのまま表示されます。


//configurable: trueの場合
delete user.fullName;
console.log(user.fullName);
//結果
undefined

//configurable: falseの場合
delete user.fullName;
console.log(user.fullName);
//結果
Kit Harington

まとめ

Getter,Setter,Object.definePropetyの設定方法を簡単な例を利用して説明を行いました。この例を理解していれば自分自身で活用しなかったとしてもコード中にこれらの用語が出てきた場合もコードの理解が進むと思います。