Exemples supplémentaires pour Object.defineProperty

Cette page illustre des exemples supplémentaires pour la méthode Object.defineProperty().

Utiliser des masques binaires (plutôt qu'un descripteur de propriété)

Si l'on souhaite définir de nombreuses propriétés via la méthode Object.defineProperty() , on peut utiliser le même descripteur pour chaque propriété en le modifiant chaque fois que c'est nécessaire avec des marqueurs binaires (flags) pour former un masque binaire.

var oDesc = {};
function defProp (nMask, oObj, sKey, vVal_fGet, fSet) {
  if (nMask & 8) {
    // descripteur d'accesseur
    if (vVal_fGet) {
      oDesc.get = vVal_fGet;
    } else {
      delete oDesc.get;
    }
    if (fSet) {
      oDesc.set = fSet;
    } else {
      delete oDesc.set;
    }
    delete oDesc.value;
    delete oDesc.writable;
  } else {
    // descripteur de données
    if (arguments.length > 3) {
      oDesc.value = vVal_fGet;
    } else {
      delete oDesc.value;
    }
    oDesc.writable = Boolean(nMask & 4);
    delete oDesc.get;
    delete oDesc.set;
  }
  oDesc.enumerable = Boolean(nMask & 1);
  oDesc.configurable = Boolean(nMask & 2);
  Object.defineProperty(oObj, sKey, oDesc);
  return oObj;
}

/*
* :: function defProp ::
*
* nMask est un masque binaire:
*  flag 0x1 : la propriété est enumérable,
*  flag 0x2 : la propriété est configurable,
*  flag 0x4 : la propriété est accessible en écriture,
*  flag 0x8 : la propriété est un descripteur d'accesseur.
* oObj est le nom de l'objet dont on souhaite définir la propriété;
* sKey est le nom de la propriété à créer ou modifier;
* vVal_fGet est la valeur à affecter pour un descripteur de donnée 
* ou l'accesseur à utiliser pour un descripteur d'accesseur 
* (selon le masque choisi)
* fSet est le mutateur à affecter pour un descripteur d'accesseur;
*
* Les valeurs possibles du masque binaire :
*
*  0  : descripteur de données en lecture seule 
*       non configurable, non énumérable (0000).
*  1  : descripteur de données en lecture seule
*       non configurable, énumérable (0001).
*  2  : descripteur de données en lecture seule
*       configurable, non énumérable (0010).
*  3  : descripteur de données en lecture seule
*       configurable, énumérable (0011).
*  4  : descripteur de données en écriture
*       non configurable, non énumérable (0100).
*  5  : descripteur de données en écriture 
*       non configurable, énumérable (0101).
*  6  : descripteur de données en écriture
*       configurable, non énumérable (0110).
*  7  : descripteur de données en écriture
*       configurable, énumérable (0111).
*  8  : descripteur d'accessseur
*       non configurable, non énumérable (1000).
*  9  : descripteur d'accessseur
*       non configurable, énumérable (1001).
*  10 : descripteur d'accessseur
*       configurable, non énumérable (1010).
*  11 :  descripteur d'accessseur
*        configurable, énumérable (1011).
*  Note : Si le flag 0x8 est mis à "descripteur d'accesseur"
*  le flag 0x4 (writable) sera ignoré. Sinon, l'argument
*  fSet sera ignoré.
*/

// créer un nouvel objet
var myObj = {};

// ajouter un descripteur de données en écriture 
// non configurable, non énumérable
defProp(4, myObj, "myNumber", 25);

// ajouter un descripteur de données en lecture seule
// non configurable, énumérable
defProp(1, myObj, "myString", "Hello world!");

// ajouter un descripteur d'accesseur
// non configurable, énumérable
defProp(9, myObj, "myArray", function() {
  for (var iBit = 0, iFlag = 1, aBoolArr = [false];
        iFlag < this.myNumber + 1 || (this.myNumber & iFlag);
        iFlag = iFlag << 1) {
    aBoolArr[iBit++] = Boolean(this.myNumber & iFlag);
  }
  return aBoolArr;
}, function(aNewMask) {
  for (var nNew = 0, iBit = 0; iBit < aNewMask.length; iBit++) {
    nNew |= Boolean(aNewMask[iBit]) << iBit;
  }
  this.myNumber = nNew;
});

// ajouter une descripteur de données en écriture (undefined)
// configurable, énumérable
defProp(7, myObj, "myUndefined");

// ajouter un descripteur d'accesseur (uniquement le getter)
// configurable, énumérable
defProp(11, myObj, "myDate", function() { return new Date(); });

// ajouter un descripteur d'accesseur (uniquement le setter)
// non configurable, non énumérable
defProp(8, myObj, "myAlert", null, function(sTxt) { alert(sTxt); });

myObj.myAlert = myObj.myDate.toLocaleString() + "\n\n" + myObj.myString +
      "\nLe nombre " + myObj.myNumber + " représente le masque binaire : " +
      myObj.myArray.join(", ") + ".";

// on liste les propriétés énumérables 
var sList = "Voici les propriétés énumérables de l'objet :\n";
for (var sProp in myObj) {
    sList += "\nmyObj." + sProp + " => " + myObj[sProp] + ";"
}

console.log(sList);

Créer une nouvelle méthode Object.setProperty() non native

On peut faire la même chose avec un descripteur d'objet obtenu via un constructeur en utilisant une méthode adaptée de Object qu'on peut appeler setProperty() :

// créer une nouvelle méthode de Object appelée Object.setProperty()

new (function() {
  var oDesc = this;
  Object.setProperty = function (nMask, oObj, sKey, vVal_fGet, fSet) {
    if (nMask & 8) {
      // accessor descriptor
      if (vVal_fGet) {
        oDesc.get = vVal_fGet;
      } else {
        delete oDesc.get;
      }
      if (fSet) {
        oDesc.set = fSet;
      } else {
        delete oDesc.set;
      }
      delete oDesc.value;
      delete oDesc.writable;
    } else {
      // data descriptor
      if (arguments.length > 3) {
        oDesc.value = vVal_fGet;
      } else {
        delete oDesc.value;
      }
      oDesc.writable = Boolean(nMask & 4);
      delete oDesc.get;
      delete oDesc.set;
    }
    oDesc.enumerable = Boolean(nMask & 1);
    oDesc.configurable = Boolean(nMask & 2);
    Object.defineProperty(oObj, sKey, oDesc);
    return oObj;
  };
})();

// on crée un objet vide
var monObjet = {};

// on ajoute un descripteur de données en écriture
// non configurable, non énumérable
Object.setProperty(4, monObjet, "monNombre", 25);

// on ajoute un descripteur de données en lecture seule
// non configurable, énumérable
Object.setProperty(1, monObjet, "maChaîne", "Hello world!");

// etc. etc. 
Note : La méthode Object.setProperty() pourrait faire partie de futures méthodes natives (voir le bug ECMAScript 335).

Syntaxe

Object.setProperty(bitmask, obj, prop[, value/getter[, setter]])

Paramètres

bitmask
Le descripteur du masque binaire (bitmask), défini ci-après.
obj
Le nom de l'objet dont on souhaite définir la propriété.
prop
Le nom de la propriété à définir ou à modifier.
value/getter
Paramètre optionnel. La valeur à affecter au descripteur de données ou l'accesseur à affecter au descripteur d'accesseur (selon le bitmask choisi).
setter
Paramètre optionnel. Le mutateur à affecter au descripteur de données. Si le marqueur situé en 0x8 est réglé pour un descripteur de données, cet argument sera ignoré.

Description

La méthode, non-native, Object.setProperty() fonctionne comme la méthode Object.defineProperty() mais utilise un descripteur via un masque binaire plutôt qu'un descripteur d'objet. L'argument bitmask a la structure suivante :

flag 0x1
La propriété est énumérable.
flag 0x2
La propriété est configurable.
flag 0x4
La propriété est accessible en écriture.
flag 0x8
La propriété est un descripteur d'accesseur.

Le descripteur en masque binaire peut donc prendre les valeurs numériques suivantes :

  • 0 : Le masque binaire représente un descripteur de données en lecture seule - non configurable, non énumérable (0000).
  • 1 : Le masque binaire représente un descripteur de données en lecture seule - non configurable, énumérable (0001).
  • 2 : Le masque binaire représente un descripteur de données en lecture seule - configurable, non énumérable (0010).
  • 3 : Le masque binaire représente un descripteur de données en lecture seule - configurable, énumérable (0011).
  • 4 : Le masque binaire représente un descripteur de données en écriture - non configurable, non énumérable (0100).
  • 5 : Le masque binaire représente un descripteur de données en écriture - non configurable, énumérable (0101).
  • 6 : Le masque binaire représente un descripteur de données en écriture - configurable, non énumérable (0110).
  • 7 : Le masque binaire représente un descripteur de données en écriture - configurable, énumérable (0111).
  • 8 : Le masque binaire représente un descripteur d'accesseur - non configurable, non énumérable (1000).
  • 9 : Le masque binaire représente un descripteur d'accesseur - non configurable, énumérable (1001).
  • 10 : Le masque binaire représente un descripteur d'accesseur - configurable, non énumérable (1010).
  • 11 : Le masque binaire représente un descripteur d'accesseur - configurable, énumérable (1011).
Note : Si le flag 0x8 est utilisé pour un descripteur d'accesseur, le flag 0x4 (writable) sera ignoré. Sinon, l'argument setter sera ignoré.

Implémentation de HTMLSelectElement.selectedIndex

La méthode Object.defineProperty() fonctionne également avec les objets natifs. L'exemple qui suit illustre comment implémenter la propriété selectedIndex de HTMLSelectElement dans les boutons radio.

<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>
  Exemple de selectedIndex pour 
  les groupes de boutons radio
</title>
<script type="text/javascript">
Object.defineProperty(NodeList.prototype, "selectedIndex", {
  get: function() {
    var nIndex = this.length - 1;
    while (nIndex > -1 && !this[nIndex].checked) {
      nIndex--;
    }
    return nIndex;
  },
  
  set: function(nNewIndex) {
    if (isNaN(nNewIndex)) {
      return;
    }
    var nOldIndex = this.selectedIndex;
    if (nOldIndex > -1) {
      this[nOldIndex].checked = false;
    }
    if (nNewIndex > -1) {
      this[nNewIndex].checked = true;
    }
  },
  
  enumerable: true,
  configurable: false
});

// on essaie!
function checkForm() {
  var nSelectedIndex = document.myForm.myRadioGroup.selectedIndex;
  if (nSelectedIndex < 0) {
    alert("Choisir un élément !!");
    return false;
  }
  alert("Félicitations !! Vous avez choisi le " +
        document.myForm.myRadioGroup[nSelectedIndex].value + "."
        );
  return true;
}
</script>
</head>

<body>          
  <form name="myForm" onsubmit="return(checkForm());">
    <fieldset><legend>Choisir un élément</legend>
      <p>
      <input type="radio" name="myRadioGroup" id="ourShirt" value="chemise"/>
      <label for="ourShirt">chemise</label><br />
      <input type="radio" name="myRadioGroup" id="ourPants" value="pantalon"/>
      <label for="ourPants">pantalon</label><br />
      <input type="radio" name="myRadioGroup" id="ourBelt" value="ceinture"/>
      <label for="ourBelt">ceinture</label><br />
      <input type="radio" name="myRadioGroup" id="ourShoes" value="chaussures"/>
      <label for="ourShoes">chaussures</label></p>
      <p>
<span style="cursor:pointer;text-decoration:underline;color:#0000ff;" onclick="document.myForm.myRadioGroup.selectedIndex=2;">
Choisissez ce que vous préférez
</span>
      </p>
      <p><input type="submit" value="Commander !" />
    </fieldset>
  </form>
</body>
</html>

Étiquettes et contributeurs liés au document

Étiquettes : 
 Contributeurs à cette page : SphinxKnight, teoli, P45QU10U
 Dernière mise à jour par : SphinxKnight,