186

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

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

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

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

4

10 に答える 10

526

同様の質問をしたので、順を追って説明しましょう。少し長くなりますが、これを書くのに費やした時間よりもはるかに多くの時間を節約できます:

プロパティは、クライアント コードを明確に分離するために設計された 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; } 
}

getDiscount次に、 ( accessor ) およびsetDiscount( mutator ) メソッドを変更するだけです。問題は、ほとんどのメンバーが一般的な変数のように振る舞うことです。ここでは割引だけに特別な注意が必要です。しかし、優れた設計では、コードを拡張可能に保つために、すべてのデータ メンバーをカプセル化する必要があります。そのため、何もしないコードをたくさん追加する必要があります。これも悪い設計であり、ボイラープレートのアンチパターンです。後でフィールドをメソッドにリファクタリングできない場合があるため (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

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

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

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

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

  • アクセサ記述子= get + set (上記の例を参照)
    • getは関数でなければなりません。その戻り値はプロパティの読み取りに使用されます。指定されていない場合、デフォルトはundefinedであり、 undefined を返す関数のように動作します
    • setは関数でなければなりません。そのパラメーターは、プロパティに値を割り当てる際に RHS で埋められます。指定されていない場合、デフォルトはundefinedであり、空の関数のように動作します
  • データ記述子= 値 + 書き込み可能 (以下の例を参照)
    • のデフォルトは未定義です。writable configurable、およびenumerable (下記参照) が true の場合、プロパティは通常のデータ フィールドのように動作します。
    • 書き込み可能- デフォルト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データ記述子を使用してすべてのプロパティに効果的に設定されます)。セッターの書き込み可能なプロパティは影響を受けません (プロパティがないため)。フリーズは浅い: プロパティが Object の場合、そのプロパティはフリーズされていないことを意味します (必要に応じて、ディープ コピー - クローニングと同様に、「ディープ フリーズ」などを実行する必要があります)。Object.isFrozen(<yourObject>)検出するために使用

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

于 2013-09-01T10:29:06.800 に答える
27

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

console.log(player.health);

事実上、次のものと大差ありません。

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

get の反対が set で、値を代入するときに使用されます。セッターがいないため、プレイヤーの健康への割り当ては意図されていないようです:

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

于 2013-08-30T04:00:06.547 に答える
2

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

于 2013-08-30T04:02:36.337 に答える
0

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

于 2015-01-26T20:45:37.347 に答える
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);
于 2014-07-30T18:07:10.040 に答える
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
   });

ここに画像の説明を入力

define プロパティについて簡単に説明します。

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

于 2019-07-30T13:31:55.997 に答える
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

于 2019-02-24T20:44:34.810 に答える
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

于 2020-04-22T07:06:00.910 に答える