オブジェクトのプロパティには 2 種類あります。
1 つ目の種類はデータ プロパティです。私たちは彼らと協力する方法をすでに知っています。これまで使用してきたプロパティはすべてデータ プロパティでした。
2 番目のタイプのプロパティは新しいものです。これはアクセサ プロパティです。これらは本質的には値の取得と設定時に実行される関数ですが、外部コードにとっては通常のプロパティのように見えます。
アクセサ プロパティは、「getter」メソッドと「setter」メソッドによって表されます。オブジェクト リテラルでは、 get
およびset
で表されます。
obj = { にします get propName() { // getter、obj.propName の取得時に実行されるコード }、 set propName(値) { // セッター、obj.propName = value の設定時に実行されるコード } };
getter はobj.propName
が読み取られるときに機能し、setter は割り当てられるときに機能します。
たとえば、 name
とsurname
を持つuser
オブジェクトがあります。
ユーザー = { にします 名前:「ジョン」、 姓:「スミス」 };
次に、 fullName
プロパティを追加します。これは"John Smith"
でなければなりません。もちろん、既存の情報をコピー&ペーストしたくないので、それをアクセサーとして実装できます。
ユーザー = { にします 名前:「ジョン」、 姓:「スミス」、 フルネームを取得() { `${this.name} ${this.surname}`を返します。 } }; アラート(ユーザー.フルネーム); // ジョン・スミス
外側から見ると、アクセサー プロパティは通常のプロパティのように見えます。それがアクセサー プロパティの考え方です。 user.fullName
関数として呼び出すのではなく、通常どおり読み取ります。ゲッターはバックグラウンドで実行されます。
現時点では、 fullName
getter のみがあります。 user.fullName=
割り当てようとすると、エラーが発生します。
ユーザー = { にします フルネームを取得() { `...` を返します。 } }; user.fullName = "テスト"; // エラー (プロパティにはゲッターしかありません)
user.fullName
のセッターを追加して修正しましょう。
ユーザー = { にします 名前:「ジョン」、 姓:「スミス」、 フルネームを取得() { `${this.name} ${this.surname}`を返します。 }、 set fullName(値) { [この名前、この姓] = value.split(" "); } }; // set fullName は指定された値で実行されます。 user.fullName = "アリス・クーパー"; アラート(ユーザー名); // アリス アラート(ユーザー.姓); // クーパー
結果として、「仮想」プロパティfullName
が得られます。読み取りと書き込みが可能です。
アクセサー プロパティの記述子は、データ プロパティの記述子とは異なります。
アクセサー プロパティにはvalue
やwritable
はありませんが、代わりにget
関数とset
関数があります。
つまり、アクセサー記述子には次のものが含まれる場合があります。
get
– 引数のない関数。プロパティが読み取られたときに機能します。
set
– 引数が 1 つある関数。プロパティが設定されたときに呼び出されます。
enumerable
– データプロパティと同じ、
configurable
– データ プロパティの場合と同じです。
たとえば、 defineProperty
を使用してアクセサーfullName
を作成するには、 get
およびset
を使用して記述子を渡すことができます。
ユーザー = { にします 名前:「ジョン」、 姓:「スミス」 }; Object.defineProperty(user, 'fullName', { 得る() { `${this.name} ${this.surname}`を返します。 }、 セット(値) { [この名前、この姓] = value.split(" "); } }); アラート(ユーザー.フルネーム); // ジョン・スミス for(ユーザーにキーを入力させます)alert(key); // 名前、姓
プロパティは、アクセサー ( get/set
メソッドを持つ) またはデータ プロパティ ( value
を持つ) のいずれかであり、両方であることはできないことに注意してください。
同じ記述子でget
とvalue
両方を指定しようとすると、エラーが発生します。
// エラー: 無効なプロパティ記述子。 Object.defineProperty({}, 'prop', { 得る() { 1を返す }、 値: 2 });
ゲッター/セッターを「実際の」プロパティ値のラッパーとして使用すると、それらの操作をより詳細に制御できます。
たとえば、 user
に短すぎる名前を禁止したい場合は、セッターname
を用意し、その値を別のプロパティ_name
に保持することができます。
ユーザー = { にします 名前を取得() { これを返します。_name; }、 名前(値)を設定 { if (value.length < 4) { alert("名前が短すぎます。少なくとも 4 文字が必要です"); 戻る; } this._name = 値; } }; user.name = "ピート"; アラート(ユーザー名); // ピート ユーザー名 = ""; // 名前が短すぎます...
したがって、名前は_name
プロパティに保存され、アクセスはゲッターとセッターを介して行われます。
技術的には、外部コードはuser._name
を使用して名前に直接アクセスできます。ただし、アンダースコア"_"
で始まるプロパティは内部的なものであり、オブジェクトの外部からは触れるべきではないという広く知られた慣例があります。
アクセサーの優れた用途の 1 つは、「通常の」データ プロパティをゲッターとセッターに置き換え、その動作を微調整することで、いつでもそのプロパティを制御できることです。
データ プロパティname
とage
使用してユーザー オブジェクトの実装を開始したと想像してください。
関数 ユーザー(名前、年齢) { this.name = 名前; this.age = 年齢; } let john = new User("ジョン", 25); アラート( ジョン.年齢 ); // 25
…しかし、遅かれ早かれ、状況は変わるかもしれません。 age
の代わりに、 birthday
を保存することを決定することもできます。これは、より正確で便利だからです。
関数 ユーザー(名前、誕生日) { this.name = 名前; this.birthday = 誕生日; } let john = new User("John", new Date(1992, 6, 1));
さて、まだage
プロパティを使用している古いコードをどうすればよいでしょうか?
そのような箇所をすべて見つけて修正することもできますが、それには時間がかかり、そのコードが他の多くの人によって使用されている場合は困難になる可能性があります。それに加えて、 user
にage
が含まれるのは良いことですよね?
そのままにしておきましょう。
age
のゲッターを追加すると、問題が解決されます。
関数 ユーザー(名前、誕生日) { this.name = 名前; this.birthday = 誕生日; // 年齢は現在の日付と誕生日から計算されます Object.defineProperty(this, "年齢", { 得る() { 今日年 = 新しい Date().getFull Year(); にします。 今日年を返す - this.birthday.getFull Year(); } }); } let john = new User("John", new Date(1992, 6, 1)); アラート(ジョン.誕生日); // 誕生日は利用可能です アラート( ジョン.年齢 ); // ...年齢も同様
現在は古いコードも動作し、優れた追加プロパティが得られています。