web-dev-qa-db-ja.com

javascript Object.definePropertyの使用方法

Object.defineProperty メソッドの使用方法を調べましたが、適切なものが見つかりませんでした。

誰かが私に与えた コードのこのスニペット

Object.defineProperty(player, "health", {
    get: function () {
        return 10 + ( player.level * 15 );
    }
})

しかし、私はそれを理解していません。主に、getは取得できません(意図されたしゃれ)。どのように機能しますか?

157
Math chiller

類似した質問 を尋ねたので、段階的に見ていきましょう。少し長くなりますが、この記事を書くのに費やした時間よりもはるかに多くの時間を節約できます。

Propertyは、クライアントコードを完全に分離するために設計されたOOP機能です。たとえば、一部のeショップでは、次のようなオブジェクトがあります。

function Product(name,price) {
  this.name = name;
  this.price = price;
  this.discount = 0;
}

var sneakers = new Product("Sneakers",20); // {name:"Sneakers",price:20,discount:0}
var tshirt = new Product("T-shirt",10);  // {name:"T-shirt",price:10,discount:0}

次に、クライアントコード(eショップ)で、製品に割引を追加できます。

function badProduct(obj) { obj.discount+= 20; ... }
function generalDiscount(obj) { obj.discount+= 10; ... }
function distributorDiscount(obj) { obj.discount+= 15; ... }

後で、eショップの所有者は、割引が80%を超えることはできないことに気付くかもしれません。ここで、クライアントコードで割引の変更が発生するたびに検索し、行を追加する必要があります

if(obj.discount>80) obj.discount = 80;

次に、eショップの所有者は、戦略をさらに変更することができます。たとえば、「顧客が再販業者の場合、最大割引は90%になります」。そして、あなたは再び複数の場所で変更を行う必要があり、さらに戦略が変更されたときはいつでもこれらの行を変更することを忘れないでください。これは悪い設計です。だからencapsulationはOOPの基本原則です。コンストラクタが次のような場合:

function Product(name,price) {
  var _name=name, _price=price, _discount=0;
  this.getName = function() { return _name; }
  this.setName = function(value) { _name = value; }
  this.getPrice = function() { return _price; }
  this.setPrice = function(value) { _price = value; }
  this.getDiscount = function() { return _discount; }
  this.setDiscount = function(value) { _discount = value; } 
}

その後、getDiscountaccessor)およびsetDiscountmutator)メソッドを変更するだけです。問題は、ほとんどのメンバーが一般的な変数のように振る舞うということです。ここでは特別な注意が必要です。ただし、優れた設計では、コードを拡張可能に保つために、すべてのデータメンバーをカプセル化する必要があります。したがって、何もしない多くのコードを追加する必要があります。これも悪いデザインで、ボイラープレートのアンチパターンです。フィールドを後でメソッドにリファクタリングできない場合があります(eshopコードが大きくなるか、一部のサードパーティコードが古いバージョンに依存する場合があります)。それでも、それは悪です。これが、プロパティが多くの言語に導入された理由です。元のコードを保持し、割引メンバーをgetおよびsetブロックを持つプロパティに変換するだけです。

function Product(name,price) {
  this.name = name;
  this.price = price;
//this.discount = 0; // <- remove this line and refactor with the code below
  var _discount; // private member
  Object.defineProperty(this,"discount",{
    get: function() { return _discount; },
    set: function(value) { _discount = value; if(_discount>80) _discount = 80; }
  });
}

// the client code
var sneakers = new Product("Sneakers",20);
sneakers.discount = 50; // 50, setter is called
sneakers.discount+= 20; // 70, setter is called
sneakers.discount+= 20; // 80, not 90!
alert(sneakers.discount); // getter is called

最後の1行に注意してください。正しい割引値の責任は、クライアントコード(eショップ定義)から製品定義に移されました。製品は、データメンバーの一貫性を維持する責任があります。コードが私たちの考えと同じように機能する場合、良いデザインは(大まかに言って)です。

プロパティについてはこれだけです。しかし、javascriptはC#のような純粋なオブジェクト指向言語とは異なり、機能のコーディングが異なります。

C#では、フィールドのプロパティへの変換は breaking change であるため、パブリックフィールドは Auto-実装されたプロパティ コードが個別にコンパイルされたクライアントで使用される可能性がある場合。

Javascriptでは、標準のプロパティ(上記のゲッターとセッターを持つデータメンバー)はアクセサー記述子によって定義されます(質問にあるリンク内)。排他的に、データ記述子を使用できます(つまり、valueおよびset同じプロパティで):

  • アクセッサ記述子= get + set(上記の例を参照)
    • getは関数でなければなりません;戻り値は、プロパティの読み取りに使用されます。指定しない場合、デフォルトはundefinedで、undefinedを返す関数のように動作します
    • setは関数でなければなりません;プロパティに値を割り当てる際に、そのパラメーターにRHSが入力されます。指定しない場合、デフォルトはundefinedで、空の関数のように動作します
  • データ記述子=値+書き込み可能(以下の例を参照)
    • valuedefaultundefined; writableconfigurable、およびenumerable(以下を参照)がtrueの場合、プロパティは通常のデータフィールドのように動作します
    • writable-デフォルトfalse;そうでない場合true、プロパティは読み取り専用です。書き込みの試行はエラーなしで無視されます*!

両方の記述子に次のメンバーを含めることができます。

  • configurable-デフォルトfalse;正しくない場合、プロパティは削除できません。削除しようとしてもエラーなしで無視されます*!
  • enumerable-デフォルトfalse; trueの場合、for(var i in theObject)で繰り返されます。 falseの場合、反復されませんが、パブリックとしてアクセス可能です。

* strict mode の場合を除いて-その場合、JSは try-catch block で捕捉されない限りTypeErrorで実行を停止します

これらの設定を読み取るには、Object.getOwnPropertyDescriptor()を使用します。

例で学ぶ:

var o = {};
Object.defineProperty(o,"test",{
  value: "a",
  configurable: true
});
console.log(Object.getOwnPropertyDescriptor(o,"test")); // check the settings    

for(var i in o) console.log(o[i]); // nothing, o.test is not enumerable
console.log(o.test); // "a"
o.test = "b"; // o.test is still "a", (is not writable, no error)
delete(o.test); // bye bye, o.test (was configurable)
o.test = "b"; // o.test is "b"
for(var i in o) console.log(o[i]); // "b", default fields are enumerable

クライアントコードにチートを許可したくない場合は、3つのレベルの制限によってオブジェクトを制限できます。

  • Object.preventExtensions(yourObject)は、新しいプロパティがyourObjectObject.isExtensible(<yourObject>)を使用して、オブジェクトでメソッドが使用されたかどうかを確認します。防止はshallowです(下記参照)。
  • Object.seal(yourObject)上記と同じで、プロパティを削除することはできません(すべてのプロパティにconfigurable: falseを効果的に設定します)。 Object.isSealed(<yourObject>)を使用して、オブジェクトのこの機能を検出します。シールはshallowです(下記参照)。
  • Object.freeze(yourObject)上記と同じで、プロパティは変更できません(データ記述子を使用して、すべてのプロパティにwritable: falseを効果的に設定します)。 Setterの書き込み可能なプロパティは影響を受けません(プロパティがないため)。フリーズはshallow:プロパティがObjectの場合、そのプロパティはフリーズされないことを意味します(必要に応じて、「ディープフリーズ」のような何かを実行する必要があります- ディープコピー-クローニング )。 Object.isFrozen(<yourObject>)を使用して検出します。

ほんの数行を楽しく書くだけで、これを気にする必要はありません。しかし、リンクされた質問で述べたように、ゲームをコーディングしたい場合は、本当に良いデザインに気を配るべきです。 アンチパターンコードの匂いについてグーグルを試してみてください。 「ああ、コードを完全に書き直す必要があります!」のような状況を回避するのに役立ちます。多くのコードを作成したい場合、絶望の月を節約できます。がんばろう。

452
Jan Turoň

getは、次のように、値player.healthを読み取ろうとするときに呼び出される関数です。

console.log(player.health);

事実上、以下とそれほど変わりません:

player.getHealth = function(){
  return 10 + this.level*15;
}
console.log(player.getHealth());

値の割り当て時に使用されるgetの反対が設定されます。セッターがないため、プレーヤーのヘルスへの割り当ては意図されていないようです:

player.health = 5; // Doesn't do anything, since there is no set function defined

非常に簡単な例:

var player = {
  level: 5
};

Object.defineProperty(player, "health", {
  get: function() {
    return 10 + (player.level * 15);
  }
});

console.log(player.health); // 85
player.level++;
console.log(player.health); // 100

player.health = 5; // Does nothing
console.log(player.health); // 100
23
Paulpro

definePropertyはObjectのメソッドであり、いくつかの基準を満たすようにプロパティを設定できます。 firstNameとlastNameの2つのプロパティを持つ従業員オブジェクトの簡単な例を次に示し、オブジェクトのtoStringメソッドをオーバーライドして2つのプロパティを追加します。

var employee = {
    firstName: "Jameel",
    lastName: "Moideen"
};
employee.toString=function () {
    return this.firstName + " " + this.lastName;
};
console.log(employee.toString());

次のように出力されます:Jameel Moideen

オブジェクトでdefinePropertyを使用して同じコードを変更します

var employee = {
    firstName: "Jameel",
    lastName: "Moideen"
};
Object.defineProperty(employee, 'toString', {
    value: function () {
        return this.firstName + " " + this.lastName;
    },
    writable: true,
    enumerable: true,
    configurable: true
});
console.log(employee.toString());

最初のパラメーターはオブジェクトの名前で、2番目のパラメーターは追加するプロパティの名前です。この場合はtoStringで、最後のパラメーターはjsonオブジェクトで、値は関数になり、3つのパラメーターは書き込み可能、​​列挙可能ですそして今、私はすべてを真と宣言しました。

この例を実行すると、次のように出力されます:Jameel Moideen

書き込み可能、​​列挙可能、構成可能の3つのプロパティが必要な理由を理解しましょう。

書き込み可能

Javascriptの非常に迷惑な部分の1つは、toStringプロパティを他の何かに変更した場合です

enter image description here

これを再度実行すると、すべてが壊れます。書き込み可能をfalseに変更しましょう。同じものを再度実行すると、「Jameel Moideen」として正しい出力が得られます。このプロパティは、後でこのプロパティが上書きされるのを防ぎます。

列挙可能

オブジェクト内のすべてのキーを印刷すると、toStringを含むすべてのプロパティを確認できます。

console.log(Object.keys(employee));

enter image description here

enumerableをfalseに設定すると、他のすべてのユーザーに対してtoStringプロパティを非表示にできます。これを再度実行すると、firstName、lastNameが取得されます

設定可能

誰かが後でオブジェクトを再定義して、たとえば、enumerableをtrueに設定して実行した場合。 toStringプロパティが再び表示されます。

var employee = {
    firstName: "Jameel",
    lastName: "Moideen"
};
Object.defineProperty(employee, 'toString', {
    value: function () {
        return this.firstName + " " + this.lastName;
    },
    writable: false,
    enumerable: false,
    configurable: true
});

//change enumerable to false
Object.defineProperty(employee, 'toString', {

    enumerable: true
});
employee.toString="changed";
console.log(Object.keys(employee));

enter image description here

構成可能をfalseに設定することにより、この動作を制限できます。

この情報の元の参照は私の個人的なブログからです

10
Code-EZ

基本的に、definePropertyは、オブジェクト、プロパティ、記述子の3つのパラメーターを受け取るメソッドです。この特定の呼び出しで行われているのは、playerオブジェクトの"health"プロパティが、プレーヤーオブジェクトのレベルの10プラス15倍に割り当てられていることです。

3
Cole Pilegard

オブジェクトに新しいプロパティを直接定義するか、オブジェクトの既存のプロパティを変更して、オブジェクトを返します。

注:このメソッドは、Object型のインスタンスではなく、Objectコンストラクターで直接呼び出します。

   const object1 = {};
   Object.defineProperty(object1, 'property1', {
      value: 42,
      writable: true,
      enumerable: true,
      configurable: true
   });

enter image description here

プロパティの定義に関する簡単な説明。

0
Mano

概要:

Object.defineProperty(player, "health", {
    get: function () {
        return 10 + ( player.level * 15 );
    }
});

Object.definePropertyは、プレーヤーオブジェクトに新しいプロパティを作成するために使用されます。 Object.definePropertyは、JSランタイム環境にネイティブに存在する関数であり、次の引数を取ります。

Object.defineProperty(obj, prop, descriptor)

  1. 新しいプロパティを定義するobject
  2. 新しいプロパティの名前定義したい
  3. 記述子オブジェクト

記述子オブジェクトは興味深い部分です。ここでは、次のものを定義できます。

  1. 設定可能<boolean>trueの場合、プロパティ記述子が変更され、プロパティがオブジェクトから削除される場合があります。 configurableがfalseの場合、Object.definePropertyで渡される記述子プロパティーは変更できません。
  2. 書き込み可能<boolean>trueの場合、割り当て演算子を使用してプロパティを上書きできます。
  3. 列挙可能<boolean>trueの場合、for...inループでプロパティを反復処理できます。また、Object.keys関数を使用すると、キーが存在します。プロパティがfalseである場合、for..inループを使用して反復されることはなく、Object.keysを使用する場合は表示されません。
  4. get<function>:プロパティが必要なときに呼び出される関数。直接値を与える代わりに、この関数が呼び出され、戻り値がプロパティの値として与えられます
  5. set<function>:プロパティが割り当てられるたびに呼び出される関数。直接値を設定する代わりに、この関数が呼び出され、返された値を使用してプロパティの値が設定されます。

例:

const player = {
  level: 10
};

Object.defineProperty(player, "health", {
  configurable: true,
  enumerable: false,
  get: function() {
    console.log('Inside the get function');
    return 10 + (player.level * 15);
  }
});

console.log(player.health);
// the get function is called and the return value is returned as a value

for (let prop in player) {
  console.log(prop);
  // only prop is logged here, health is not logged because is not an iterable property.
  // This is because we set the enumerable to false when defining the property
}
0
import { CSSProperties } from 'react'
import { BLACK, BLUE, GREY_DARK, WHITE } from '../colours'

export const COLOR_ACCENT = BLUE
export const COLOR_DEFAULT = BLACK
export const FAMILY = "'Segoe UI', sans-serif"
export const SIZE_LARGE = '26px'
export const SIZE_MEDIUM = '20px'
export const WEIGHT = 400

type Font = {
  color: string,
  size: string,
  accent: Font,
  default: Font,
  light: Font,
  neutral: Font,
  xsmall: Font,
  small: Font,
  medium: Font,
  large: Font,
  xlarge: Font,
  xxlarge: Font
} & (() => CSSProperties)

function font (this: Font): CSSProperties {
  const css = {
    color: this.color,
    fontFamily: FAMILY,
    fontSize: this.size,
    fontWeight: WEIGHT
  }
  delete this.color
  delete this.size
  return css
}

const dp = (type: 'color' | 'size', name: string, value: string) => {
  Object.defineProperty(font, name, { get () {
    this[type] = value
    return this
  }})
}

dp('color', 'accent', COLOR_ACCENT)
dp('color', 'default', COLOR_DEFAULT)
dp('color', 'light', COLOR_LIGHT)
dp('color', 'neutral', COLOR_NEUTRAL)
dp('size', 'xsmall', SIZE_XSMALL)
dp('size', 'small', SIZE_SMALL)
dp('size', 'medium', SIZE_MEDIUM)

export default font as Font
0
Alvin Smith

はい、セットアップセッターとゲッターの拡張機能はこれが私の例ですObject.defineProperty(obj、name、func)

var obj = {};
['data', 'name'].forEach(function(name) {
    Object.defineProperty(obj, name, {
        get : function() {
            return 'setter & getter';
        }
    });
});


console.log(obj.data);
console.log(obj.name);
0
Faizal Pribadi

Object.defineProperty()はグローバル関数です。それ以外の場合はオブジェクトを宣言する関数内では使用できません。静的に使用する必要があります...

0
ISONecroMAn