javascript Object.definePropertyの使用方法


183

私はObject.definePropertyメソッドの使い方を探しましたが、適切なものは見つかりませんでした。

誰かが私にこのコードのスニペットをくれました:

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

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


1
developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…これは、ここでの優れたチュートリアルです。
Martian2049

回答:


499

同様の質問をしたので、少しずつ見ていきましょう。少し長くなりますが、これを書くのに費やした時間よりもはるかに多くの時間を節約できます。

プロパティは、クライアントコードを完全に分離するために設計された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%になる可能性があります」のように、さらに戦略を変更できます。そして、あなたは再び複数の場所で変更を行う必要があり、さらに戦略が変更されたときはいつでもこれらの行を変更することを覚えておく必要があります。これは悪いデザインです。それが、カプセル化が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コードが大きくなるか、一部のサードパーティコードが古いバージョンに依存する可能性があるため)。しかし、それでも、それは悪です。これが、プロパティが多くの言語に導入された理由です。元のコードを保持して、割引メンバーをプロパティに変換するだけですgetsetブロック:

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#では、フィールドをプロパティに変換することは重大な変更です。そのため、コードが個別にコンパイルされたクライアントで使用される可能性がある場合は、パブリックフィールドを自動実装プロパティとしてコーディングする必要があります。

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

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

どちらの記述子にも次のメンバーを含めることができます。

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

* strictモードでない限り-その場合、try-catchブロックでキャッチされない限り、JSは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)は、新しいプロパティが yourObjectに追加されないようにします。使用Object.isExtensible(<yourObject>)方法は、オブジェクトに使用されたかどうかを確認します。予防は浅いです(以下をお読みください)。
  • 上記と同じ Object.seal(yourObject)とプロパティは削除できません(configurable: falseすべてのプロパティに効果的に設定されます)。Object.isSealed(<yourObject>)オブジェクトのこの機能を検出するために使用します。シールは浅いです(以下を参照)。
  • 上記と同じ Object.freeze(yourObject)とプロパティは変更できません(writable: falseデータ記述子を使用してすべてのプロパティに効果的に設定されます)Setterの書き込み可能なプロパティは影響を受けません(プロパティがないため)。フリーズは浅いです。つまり、プロパティがObjectの場合、そのプロパティはフリーズされないことを意味します(必要に応じて、ディープコピー-クローニングと同様に、「ディープフリーズ」などを実行する必要があります)。Object.isFrozen(<yourObject>)それを検出するために使用します。

ほんの数行を書くだけなら、これを気にする必要はありません。しかし、(リンクされた質問で述べたように)ゲームをコーディングしたい場合は、優れたデザインを本当に気にする必要があります。アンチパターンコードの匂いについてグーグルで試してみてください。「ああ、コードをもう一度完全に書き直す必要がある」などの状況を回避するのに役立ちます、多くのコードを作成したい場合は、何ヶ月にもわたる絶望を救うことができます。幸運を。


この部分は明らかです。 function Product(name,price) { this.name = name; this.price = price; var _discount; // private member Object.defineProperty(this,"discount",{ get: function() { return _discount; }, set: function(value) { _discount = value; if(_discount>80) _discount = 80; } }); } 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
abu abu

27

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


それは、実際()に呼び出すために使用する必要がない関数のようなものです...彼らがこのことを発明したときのアイデアが何であったのか理解できません。関数はまったく同じです:jsbin.com/bugipi/edit
js,console,

15

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、最後のパラメーターは関数になる値と書き込み可能な3つのパラメーターを持つ列挙型オブジェクトですjsonオブジェクトですとconfigurable.Right今私はすべてをtrueとして宣言しました。

例を実行すると、次のような出力が得られます:Jameel Moideen

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

書き込み可能

JavaScriptの非常に煩わしい部分の1つは、たとえばtoStringプロパティを他の何かに変更した場合です。

ここに画像の説明を入力してください

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

列挙可能な

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

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

ここに画像の説明を入力してください

enumerableをfalseに設定すると、他のユーザーからtoStringプロパティを隠すことができます。これをもう一度実行すると、firstName、lastNameを取得します

構成可能

誰かが後でオブジェクトを後で再定義した場合、たとえば、列挙可能を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));

ここに画像の説明を入力してください

この動作を制限するには、configurableをfalseに設定します。

この情報の最初の参照は私の個人ブログからです


1
あなたはこれをあなたのブログに載せてここに貼り付けただけだと思いますが、少なくとも将来のためにこれを知っています:スクリーンキャップはSOでは人気がありません。コードをコピーして貼り付けて試すことはできません。コードは検索エンジンや支援技術からは見えません。
ドミノ

@JacqueGoupil正解です。iはスクリーンショットの代わりにコードを追加して更新します
Code-EZ

3

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


0

はいいいえセットアップセッターとゲッターのために拡張された関数ですこれは私の例です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

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


0

概要:

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

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

Object.defineProperty(obj, prop, descriptor)

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

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

  1. configurable <boolean>true プロパティ記述子が変更され、プロパティがオブジェクトから削除される場合。構成可能な場合false、渡される記述子プロパティはObject.defineProperty変更できません。
  2. 書き込み可能 <boolean>trueプロパティが代入演算子を使用して上書きされる可能性がある場合。
  3. Enumerable <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

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

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

   const object1 = {};
   Object.defineProperty(object1, 'property1', {
      value: 42,
      writable: false, //If its false can't modify value using equal symbol
      enumerable: false, // If its false can't able to get value in Object.keys and for in loop
      configurable: false //if its false, can't able to modify value using defineproperty while writable in false
   });

ここに画像の説明を入力してください

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

コード例:https : //jsfiddle.net/manoj_antony32/pu5n61fs/


0

Object.defineProperty(Array.prototype, "last", {
  get: function() {
    if (this[this.length -1] == undefined) { return [] }
    else { return this[this.length -1] }
  }
});

console.log([1,2,3,4].last) //returns 4

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.