オブジェクトプロパティには2つの種類があります。 私たちはすでにそれらをどのように扱うか知っています。

プロパティの2番目のタイプは新しいものです。 それはアクセッサープロパティです。

ゲッターとセッター

アクセッサープロパティは「ゲッター」「セッター」メソッドで表される。 オブジェクトリテラルでは、getsetで示されます:

let obj = { get propName() { // getter, the code executed on getting obj.propName }, set propName(value) { // setter, the code executed on setting obj.propName = value }};

ゲッターはobj.propNameが読まれたとき、セッターはそれが割り当てられるときに動作します。

例えば、userオブジェクトにnamesurnameがあるとします:

let user = { name: "John", surname: "Smith"};

ここで"John Smith"とすべき、fullNameプロパティを追加したいのですが、このプロパティはです。 もちろん、既存の情報をコピーペーストしたくないので、アクセッサとして実装します:

let user = { name: "John", surname: "Smith", get fullName() { return `${this.name} ${this.surname}`; }};alert(user.fullName); // John Smith

外から見ると、アクセッサのプロパティは通常のものと同じに見えますね。 それがアクセサ・プロパティの考え方です。 user.fullName を関数として呼び出すのではなく、普通に読みます:ゲッターは裏で動いています。 user.fullName= を代入しようとするとエラーになります:

let user = { get fullName() { return `...`; }};user.fullName = "Test"; // Error (property has only a getter)

user.fullNameにセッターを追加して修正しましょう。

let user = { name: "John", surname: "Smith", get fullName() { return `${this.name} ${this.surname}`; }, set fullName(value) { = value.split(" "); }};// set fullName is executed with the given value.user.fullName = "Alice Cooper";alert(user.name); // Alicealert(user.surname); // Cooper

その結果、「仮想」プロパティfullNameが出来上がりました。 読み書きが可能です。

アクセッサ記述子

アクセッサプロパティの記述子は、データプロパティの記述子と異なります。

アクセッサプロパティには、valuewritableがなく、代わりにgetset関数が存在するのです。

つまり、アクセッサ記述子は以下のものを持つことができる:

  • get – 引数のない関数、プロパティが読まれたときに動作する、
  • set – 引数が1つの関数、プロパティが設定されたときに呼ばれる、
  • enumerable – データのプロパティと同じ、
  • configurable – データのプロパティと同じ。

例えば、definePropertyでアクセサfullNameを作成するには、getsetでディスクリプタを渡します。

let user = { name: "John", surname: "Smith"};Object.defineProperty(user, 'fullName', { get() { return `${this.name} ${this.surname}`; }, set(value) { = value.split(" "); }});alert(user.fullName); // John Smithfor(let key in user) alert(key); // name, surname

プロパティはアクセッサ(get/setメソッドを持つ)かデータプロパティ(valueを持つ)のどちらかで、両方ではないことに注意してください。

getvalue の両方を同じ記述子で供給しようとすると、エラーになります。

// Error: Invalid property descriptor.Object.defineProperty({}, 'prop', { get() { return 1 }, value: 2});

Smarter getters/setters

Getters/setters は “real” property value 上のラッパーとして使用でき、それらの操作に対してより多くの制御を得ることが可能です。

たとえば、user のあまりに短い名前を禁止したい場合、name というセッターを持ち、値は別のプロパティ _name に保持できます:

let user = { get name() { return this._name; }, set name(value) { if (value.length < 4) { alert("Name is too short, need at least 4 characters"); return; } this._name = value; }};user.name = "Pete";alert(user.name); // Peteuser.name = ""; // Name is too short...

つまり、名前はプロパティ _name に格納され、アクセスには getter と setter が使用されるわけですね。

技術的には、外部コードはuser._nameを使用して直接名前にアクセスすることができます。 しかし、アンダースコア "_" で始まるプロパティは内部的なものであり、オブジェクトの外部から触れてはならないという広く知られた慣習があります。

Using for compatibility

アクセサの素晴らしい使い方の 1 つは、ゲッターとセッターに置き換えることによって「通常の」データ プロパティをいつでも制御できるようにし、その動作を微調整できるというものです。

データ プロパティ nameage を使用してユーザー オブジェクトを実装し始めたと想像してください:

function User(name, age) { this.name = name; this.age = age;}let john = new User("John", 25);alert( john.age ); // 25

… しかし、遅かれ早かれ状況は変化するかもしれません。 age の代わりに、より正確で便利な birthday を保存することにするかもしれません:

function User(name, birthday) { this.name = name; this.birthday = birthday;}let john = new User("John", new Date(1992, 6, 1));

さて、まだ age プロパティを使う古いコードをどうするか?

そういうところを全部見つけて修正しようとすることもできますが、時間がかかり、多くの他の人々がそのコードを使っていれば難しいでしょう。 それに、ageuserにあるのはいいことでしょう?

残しておきましょうよ。

age のゲッターを追加することで問題は解決します:

function User(name, birthday) { this.name = name; this.birthday = birthday; // age is calculated from the current date and birthday Object.defineProperty(this, "age", { get() { let todayYear = new Date().getFullYear(); return todayYear - this.birthday.getFullYear(); } });}let john = new User("John", new Date(1992, 6, 1));alert( john.birthday ); // birthday is availablealert( john.age ); // ...as well as the age

これで古いコードも動作し、素晴らしい追加プロパティができました:

コメントを残す

メールアドレスが公開されることはありません。