web-dev-qa-db-fra.com

comment utiliser javascript Object.defineProperty

J'ai regardé autour de moi pour savoir comment utiliser la méthode Object.defineProperty , mais je n'ai rien trouvé de décent.

Quelqu'un m'a donné cet extrait de code :

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

Mais je ne le comprends pas. Principalement, la get est ce que je ne peux pas obtenir (jeu de mots voulu). Comment ça marche?

161
Math chiller

Puisque vous avez posé un question similaire , procédons étape par étape. C'est un peu plus long, mais cela peut vous faire gagner beaucoup plus de temps que j'ai passé à écrire ceci:

La propriété est une fonction OOP conçue pour une séparation nette du code client. Par exemple, dans certains e-shop, vous pourriez avoir des objets comme celui-ci:

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}

Ensuite, dans votre code client (l'e-shop), vous pouvez ajouter des remises à vos produits:

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

Plus tard, le propriétaire de la boutique en ligne pourrait se rendre compte que le rabais ne peut être supérieur à 80%. Maintenant, vous devez rechercher CHAQUE occurrence de la modification de la réduction dans le code client et ajouter une ligne.

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

Ensuite, le propriétaire de la boutique en ligne peut modifier davantage sa stratégie, par exemple "si le client est revendeur, la remise maximale peut être de 90%". Et vous devez refaire le changement à plusieurs endroits et vous devez vous rappeler de modifier ces lignes chaque fois que la stratégie est modifiée. C'est une mauvaise conception. C'est pourquoi encapsulation est le principe de base de la programmation orientée objet. Si le constructeur était comme ça:

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; } 
}

Ensuite, vous pouvez simplement modifier les méthodes getDiscount (accessor) et setDiscount (mutator). Le problème est que la plupart des membres se comportent comme des variables communes, mais la remise nécessite une attention particulière ici. Mais une bonne conception nécessite l’encapsulation de chaque membre de données pour que le code soit extensible. Vous devez donc ajouter beaucoup de code qui ne fait rien. C’est aussi une mauvaise conception, un antipattern de type blindé. Parfois, vous ne pouvez pas simplement refactoriser les champs en méthodes plus tard (le code de la eshop peut grossir ou un code tiers peut dépendre de l'ancienne version), de sorte que le passe-partout est un moindre mal ici. Mais quand même, c'est mal. C'est pourquoi les propriétés ont été introduites dans de nombreuses langues. Vous pouvez conserver le code d'origine, il suffit de transformer le membre discount dans une propriété avec les blocs get et 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

Notez l'avant dernière ligne: la responsabilité de la valeur de remise correcte a été déplacée du code client (définition du magasin en ligne) vers la définition du produit. Le produit est responsable de la cohérence de ses membres de données. Un bon design est (approximativement dit) si le code fonctionne de la même manière que nos pensées.

Tant de choses sur les propriétés. Mais javascript est différent des langages orientés objet purs comme C # et code les fonctionnalités différemment:

En C # , la transformation de champs en propriétés est un changement important , les champs publics doivent donc être codés comme suit: Auto- Propriétés implémentées si votre code peut être utilisé dans un client compilé séparément.

En Javascript , les propriétés standard (données avec getter et setter décrites ci-dessus) sont définies par descripteur d'accesseur (dans le lien vous avez dans votre question). Exclusivement, vous pouvez utiliser descripteur de données (vous ne pouvez donc pas utiliser valeur et set ​​sur la même propriété):

  • descripteur d'accès = get + set (voir l'exemple ci-dessus)
    • get ​​ doit être une fonction; sa valeur de retour est utilisée lors de la lecture de la propriété; si non spécifié, la valeur par défaut est ndefined, qui se comporte comme une fonction renvoyant undefined
    • set ​​ doit être une fonction; son paramètre est rempli avec RHS dans l'attribution d'une valeur à la propriété; si non spécifié, la valeur par défaut est ndefined, qui se comporte comme une fonction vide
  • descripteur de données = valeur + écriture (voir l'exemple ci-dessous)
    • valeur défaut ndefined; si writeable, configurable et énumérable (voir ci-dessous) sont vrais, la propriété se comporte comme un champ de données ordinaire
    • accessible en écriture - défaut false; sinon true, la propriété est en lecture seule; La tentative d'écriture est ignorée sans erreur *!

Les deux descripteurs peuvent avoir ces membres:

  • configurable - défaut false; sinon, la propriété ne peut pas être supprimée; La tentative de suppression est ignorée sans erreur *!
  • énumérable - défaut false; si true, il sera itéré dans for(var i in theObject); si faux, il ne sera pas itéré, mais il est toujours accessible en public

* à moins que dans mode strict - dans ce cas, JS arrête l'exécution avec TypeError sauf si elle est interceptée dans bloc try-catch

Pour lire ces paramètres, utilisez Object.getOwnPropertyDescriptor().

Apprenez par exemple:

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

Si vous ne souhaitez pas autoriser de telles astuces pour le code client, vous pouvez limiter l'objet à trois niveaux de confinement:

  • Object.preventExtensions (yourObject) empêche l'ajout de nouvelles propriétés à yourObject. Utilisez Object.isExtensible(<yourObject>) pour vérifier si la méthode a été utilisée sur l'objet. La prévention est peu profonde (lire ci-dessous).
  • Object.seal (yourObject) Comme ci-dessus et les propriétés ne peuvent pas être supprimées (définit effectivement configurable: false sur tous Propriétés). Utilisez Object.isSealed(<yourObject>) pour détecter cette fonctionnalité sur l'objet. Le sceau est peu profond (lire ci-dessous).
  • Object.freeze (votreObjet) comme ci-dessus et les propriétés ne peuvent pas être modifiées (définit effectivement writable: false à tous propriétés avec descripteur de données). La propriété accessible en écriture du Setter n'est pas affectée (puisqu'elle n'en a pas). Le gel est shallow: cela signifie que si la propriété est Object, ses propriétés NE SONT PAS gelées (si vous le souhaitez, vous devez effectuer quelque chose comme "deep freeze", similaire à copie complète). - clonage ). Utilisez Object.isFrozen(<yourObject>) pour le détecter.

Vous n'avez pas besoin de vous embêter avec cela si vous écrivez juste quelques lignes amusantes. Mais si vous voulez coder un jeu (comme vous l'avez mentionné dans la question liée), vous devez vraiment vous préoccuper du bon design. Essayez de rechercher quelque chose à propos de antipatterns et odeur de code. Cela vous aidera à éviter des situations telles que "Oh, je dois réécrire complètement mon code à nouveau!", cela peut vous faire économiser des mois de désespoir si vous voulez beaucoup coder. Bonne chance.

460
Jan Turoň

get est une fonction appelée lorsque vous essayez de lire la valeur player.health, comme dans:

console.log(player.health);

Ce n'est effectivement pas très différent de:

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

Le contraire de get est défini, ce qui serait utilisé lorsque vous affectez la valeur. Comme il n'y a pas de setter, il semble que l'affectation à la santé du joueur ne soit pas destinée:

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

Un exemple très simple:

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 est une méthode sur Object qui vous permet de configurer les propriétés pour répondre à certains critères. Voici un exemple simple avec un objet employee avec deux propriétés firstName et lastName et ajoutez les deux propriétés en redéfinissant la méthode toString sur l'objet.

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

Vous obtiendrez une sortie en tant que: Jameel Moideen

Je vais changer le même code en utilisant defineProperty sur l'objet

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());

Le premier paramètre est le nom de l'objet et le second paramètre est le nom de la propriété que nous ajoutons. Dans notre cas, il s'agit de toString, puis le dernier paramètre est un objet json dont la valeur va être une fonction et trois paramètres accessibles en écriture, énumérables. et configurable. À l'heure actuelle, je viens de déclarer que tout était vrai.

Si vous lancez l'exemple, vous obtiendrez la sortie sous la forme: Jameel Moideen

Voyons pourquoi nous avons besoin des trois propriétés telles que accessible en écriture, énumérable et configurable.

accessible en écriture

L’un des aspects très ennuyeux du javascript est, si vous changez la propriété toString en quelque chose d’autre, par exemple

enter image description here

si vous recommencez, tout devient une pause. Changeons en écriture en false. Si vous utilisez à nouveau la même chose, vous obtiendrez la sortie correcte en tant que "Jameel Moideen". Cette propriété empêchera d'écraser cette propriété plus tard.

énumérable

si vous imprimez toutes les clés à l'intérieur de l'objet, vous pouvez voir toutes les propriétés, y compris toString.

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

enter image description here

si vous définissez enumerable sur false, vous pouvez masquer la propriété toString à tout le monde. Si vous l'exécutez à nouveau, vous obtiendrez firstName, lastName

configurable

si quelqu'un redéfinit plus tard l'objet par la suite, par exemple, énumérable à true et l'exécute. Vous pouvez voir que la propriété toString est revenue.

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

vous pouvez limiter ce comportement en définissant configurable sur false.

La référence originale de cette information provient de mon blog personnel

10
Code-EZ

Fondamentalement, defineProperty est une méthode qui prend 3 paramètres: un objet, une propriété et un descripteur. Ce qui se passe dans cet appel particulier, c'est la propriété "health" de l'objet player est affectée à 10 plus 15 fois le niveau de cet objet joueur.

3
Cole Pilegard

Définit une nouvelle propriété directement sur un objet ou modifie une propriété existante sur un objet et renvoie l'objet.

Remarque: Vous appelez cette méthode directement sur le constructeur Object plutôt que sur une instance de type Object.

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

enter image description here

Explication simple sur define property.

0
Mano

Sommaire:

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

Object.defineProperty est utilisé pour créer une nouvelle propriété sur l'objet player. Object.defineProperty est une fonction présente de manière native dans l'environnement d'exécution JS et prend les arguments suivants:

Object.defineProperty(obj, prop, descriptor)

  1. Le objet sur lequel nous voulons définir une nouvelle propriété
  2. Le nom de la nouvelle propriété nous voulons définir
  3. objet descripteur

L'objet descripteur est la partie intéressante. Ici, nous pouvons définir les choses suivantes:

  1. configurable<boolean>: Si true, le descripteur de propriété peut être modifié et la propriété peut être supprimée de l'objet. Si configurable est false, les propriétés du descripteur transmises dans Object.defineProperty ne peuvent pas être modifiées.
  2. Écriture<boolean>: Si true la propriété peut être écrasée à l'aide de l'opérateur d'affectation.
  3. Enumerable<boolean>: Si true la propriété peut être itérée dans une boucle for...in. De même, lorsque vous utilisez la fonction Object.keys, la touche est présente. Si la propriété est false, ils ne seront pas itérés à l'aide d'une boucle for..in et ne s'afficheront pas lors de l'utilisation de Object.keys.
  4. get<function>: Une fonction appelée à chaque fois que la propriété est requise. Au lieu de donner la valeur directe, cette fonction est appelée et la valeur renvoyée est donnée en tant que valeur de la propriété
  5. set<function>: Une fonction appelée chaque fois que la propriété est affectée. Au lieu de définir la valeur directe, cette fonction est appelée et la valeur renvoyée est utilisée pour définir la valeur de la propriété.

Exemple:

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
Willem van der Veen
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

oui non plus de fonction s'étendant pour setup setter & getter c'est mon exemple 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 () est une fonction globale..Il n'est pas disponible à l'intérieur de la fonction qui déclare l'objet autrement.Vous devrez l'utiliser statiquement ...

0
ISONecroMAn