comment utiliser javascript Object.defineProperty

183

J'ai cherché comment utiliser la Object.definePropertyméthode, 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 comprends pas. Principalement, getc'est ce que je ne peux pas obtenir (jeu de mots). Comment ça marche?

Refroidisseur de mathématiques
la source
1
developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… c'est un excellent tutoriel ici.
Martian2049

Réponses:

499

Puisque vous avez posé une question similaire , allons-y é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 fonctionnalité POO conçue pour une séparation nette du code client. Par exemple, dans certaines boutiques en ligne, 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 réductions sur 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 l'e-boutique pourrait se rendre compte que la remise ne peut pas être supérieure à 80%. Vous devez maintenant rechercher CHAQUE occurrence de la modification de la remise dans le code client et ajouter une ligne

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

Ensuite, le propriétaire de l'e-boutique peut encore modifier sa stratégie, comme "si le client est revendeur, la remise maximale peut être de 90%" . Et vous devez à nouveau effectuer 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 l' encapsulation est le principe de base de la POO. 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( accesseur ) et setDiscount( mutator ). Le problème est que la plupart des membres se comportent comme des variables communes, seule la remise nécessite ici une attention particulière. Mais une bonne conception nécessite l'encapsulation de chaque membre de données pour que le code reste extensible. Vous devez donc ajouter beaucoup de code qui ne fait rien. C'est aussi une mauvaise conception, un anti- modèle passe- partout . Parfois, vous ne pouvez pas simplement refactoriser les champs en méthodes plus tard (le code de l'eshop peut devenir volumineux ou un code tiers peut dépendre de l'ancienne version), donc le passe-partout est moins mal ici. Mais encore, 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 de la réduction en une propriété avecgetet setblocs:

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 la dernière ligne avant: la responsabilité de la valeur de remise correcte a été déplacée du code client (définition de la boutique en ligne) à la définition du produit. Le produit est responsable de la cohérence de ses données membres. Une bonne conception est (grosso modo) si le code fonctionne de la même manière que nos pensées.

Tant de 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 des champs en propriétés est un changement radical , donc les champs publics doivent être codés en tant que propriétés implémentées automatiquement si votre code peut être utilisé dans un client compilé séparément.

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

  • descripteur d'accesseur = get + set (voir l'exemple ci-dessus)
    • get doit être une fonction; sa valeur de retour est utilisée pour lire la propriété; s'il n'est pas spécifié, la valeur par défaut est indéfinie , qui se comporte comme une fonction qui renvoie undefined
    • set doit être une fonction; son paramètre est rempli de RHS lors de l'attribution d'une valeur à la propriété; s'il n'est pas spécifié, la valeur par défaut est indéfinie , qui se comporte comme une fonction vide
  • descripteur de données = valeur + inscriptible (voir l'exemple ci-dessous)
    • valeur par défaut non définie ; si accessible en écriture , configurable et énumérable (voir ci-dessous) est true, la propriété se comporte comme un champ de données ordinaire
    • inscriptible - faux par défaut; si ce n'est pas vrai , la propriété est en lecture seule; la tentative d'écriture est ignorée sans erreur *!

Les deux descripteurs peuvent avoir ces membres:

  • configurable - faux par défaut; si ce n'est pas vrai, la propriété ne peut pas être supprimée; la tentative de suppression est ignorée sans erreur *!
  • énumérable - faux par défaut; si c'est vrai, il sera itéré dansfor(var i in theObject); s'il est faux, il ne sera pas réitéré, mais il est toujours accessible en tant que public

* sauf en mode strict - dans ce cas, JS arrête l'exécution avec TypeError sauf s'il est intercepté dans le bloc try-catch

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

Apprenez par l'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 le client à coder de telles astuces, vous pouvez restreindre l'objet de trois niveaux de confinement:

  • Object.preventExtensions (votreObjet) empêche l' ajout de nouvelles propriétés à votreObjet . UtilisezObject.isExtensible(<yourObject>)pour vérifier si la méthode a été utilisée sur l'objet. La prévention est superficielle (lire ci-dessous).
  • Object.seal (yourObject) comme ci-dessus et les propriétés ne peuvent pas être supprimées (définissent effectivementconfigurable: falsetoutes les propriétés). UtilisezObject.isSealed(<yourObject>)pour détecter cette fonction sur l'objet. Le sceau est peu profond (lire ci-dessous).
  • Object.freeze (yourObject) comme ci-dessus et les propriétés ne peuvent pas être modifiées (définit effectivementwritable: falsetoutes les propriétés avec un descripteur de données). La propriété inscriptible du Setter n'est pas affectée (car elle n'en a pas). Le gel est superficiel : 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 au deep copy - clonage ). UtilisezObject.isFrozen(<yourObject>)pour le détecter.

Vous n'avez pas besoin de vous en soucier si vous écrivez juste quelques lignes amusantes. Mais si vous souhaitez coder un jeu (comme vous l'avez mentionné dans la question liée), vous devez vraiment vous soucier d'un bon design. Essayez de rechercher quelque chose sur Google sur les anti - modèles et l' odeur du code . Cela vous aidera à éviter des situations telles que "Oh, j'ai besoin de réécrire complètement mon code à nouveau!" , cela peut vous éviter des mois de désespoir si vous voulez beaucoup coder. Bonne chance.

Jan Turoň
la source
Cette partie est claire. 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

getest une fonction qui est 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. Puisqu'il n'y a pas de passeur, il semble que l'attribution de la santé du joueur n'est pas prévue:

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

Paul
la source
c'est comme une fonction que vous n'avez pas besoin d'utiliser ()pour appeler ... Je ne comprends pas quelle était l'idée quand ils ont inventé cette chose. Les fonctions sont totalement les mêmes: jsbin.com/bugipi/edit?js,console,output
vsync
15

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 employé avec deux propriétés firstName et lastName et ajoutez les deux propriétés en remplaçant 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 la sortie comme: 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, puis le second paramètre est le nom de la propriété que nous ajoutons, dans notre cas c'est toString et puis le dernier paramètre est un objet json qui a une valeur qui va être une fonction et trois paramètres inscriptibles, énumérables et configurable.À l'heure actuelle, je viens de déclarer tout comme vrai.

Si vous exécutez l'exemple, vous obtiendrez la sortie comme: Jameel Moideen

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

inscriptible

L'une des parties les plus ennuyeuses du javascript est, si vous changez la propriété toString en autre chose, par exemple

entrez la description de l'image ici

si vous l'exécutez à nouveau, tout est interrompu. Changeons inscriptible en false. Si vous exécutez à nouveau la même chose, vous obtiendrez la sortie correcte en tant que «Jameel Moideen». Cette propriété empêchera de remplacer cette propriété ultérieurement.

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

entrez la description de l'image ici

si vous définissez enumerable sur false, vous pouvez masquer la propriété toString à tous les autres. Si vous exécutez à nouveau, vous obtiendrez firstName, lastName

configurable

si quelqu'un a redéfini ultérieurement l'objet plus tard, par exemple énumérable sur 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));

entrez la description de l'image ici

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

La référence originale de ces informations provient de mon blog personnel

Code-EZ
la source
1
Je comprends que vous aviez ceci sur votre blog et que vous venez de le coller ici, mais au moins je le sais pour l'avenir: les captures d'écran ne sont pas populaires sur SO. Vous ne pouvez pas copier du code pour l'essayer et le code ne sera pas vu par les moteurs de recherche ou les technologies d'assistance.
Domino
@JacqueGoupil You are right.i va mettre à jour en ajoutant du code au lieu de la capture d'écran
Code-EZ
3

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

Cole Pilegard
la source
0

oui plus de fonction d'extension pour le setter de configuration et le getter ceci 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);
Faizal Pribadi
la source
0

Object.defineProperty () est une fonction globale ... Elle n'est pas disponible dans la fonction qui déclare l'objet autrement, vous devrez l'utiliser de manière statique ...

ISONecroMAn
la source
0

Résumé:

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

Object.definePropertyest utilisé pour créer une nouvelle propriété sur l'objet joueur. Object.definePropertyest une fonction qui est nativement présente dans l'environnement d'exécution JS et prend les arguments suivants:

Object.defineProperty(obj, prop, descriptor)

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

L'objet descripteur est la partie intéressante. Ici, nous pouvons définir les éléments suivants:

  1. configurable <boolean> : si true le descripteur de propriété peut être modifié et que la propriété peut être supprimée de l'objet. Si configurable est, falseles propriétés du descripteur qui sont transmises Object.definePropertyne peuvent pas être modifiées.
  2. Inscriptible <boolean> : si truela propriété peut être écrasée à l'aide de l'opérateur d'affectation.
  3. Énumérable <boolean> : si true la propriété peut être itérée dans une for...inboucle. Aussi lors de l'utilisation de la Object.keysfonction, la touche sera présente. Si la propriété est, falseils ne seront pas itérés à l'aide d'une for..inboucle et n'apparaîtront pas lors de l'utilisation Object.keys.
  4. get <function> : Une fonction qui est 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 comme valeur de la propriété
  5. set <function> : une fonction qui est 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
}

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

Alvin Smith
la source
0

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

entrez la description de l'image ici

Explication simple sur la définition de la propriété.

Exemple de code: https://jsfiddle.net/manoj_antony32/pu5n61fs/

Mano
la source
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

Jaeyson Anthony Y.
la source