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.
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
).
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>