Additional examples for Object.defineProperty

This page provides additional examples for Object.defineProperty().

Using binary flags instead of a property descriptor object

If you have to define many properties through the Object.defineProperty() method, you can use the same descriptor object for each property, redefining it from time to time through binary flags.

JavaScript
var oDesc = {};
function setProp (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;
}

/*
* :: function setProp ::
*
* nMask is a bitmask:
*  flag 0x1: property is enumerable,
*  flag 0x2: property is configurable,
*  flag 0x4: property is writable,
*  flag 0x8: property is accessor descriptor.
* oObj is the object on which to define the property;
* sKey is the name of the property to be defined or modified;
* vVal_fGet is the value to assign to a data descriptor or the getter function
* to assign to an accessor descriptor (depending on the bitmask);
* fSet is the setter function to assign to an accessor descriptor;
*
* Bitmask possible values:
*
*  0  : readonly data descriptor - not configurable, not enumerable (0000).
*  1  : readonly data descriptor - not configurable, enumerable (0001).
*  2  : readonly data descriptor - configurable, not enumerable (0010).
*  3  : readonly data descriptor - configurable, enumerable (0011).
*  4  : writable data descriptor - not configurable, not enumerable (0100).
*  5  : writable data descriptor - not configurable, enumerable (0101).
*  6  : writable data descriptor - configurable, not enumerable (0110).
*  7  : writable data descriptor - configurable, enumerable (0111).
*  8  : accessor descriptor - not configurable, not enumerable (1000).
*  9  : accessor descriptor - not configurable, enumerable (1001).
*  10 : accessor descriptor - configurable, not enumerable (1010).
*  11 : accessor descriptor - configurable, enumerable (1011).
*
*  Note: If the flag 0x8 is setted to "accessor descriptor" the flag 0x4 (writable)
*  will be ignored. If not, the fSet argument will be ignored.
*/

// creating a new empty object
var myObj = {};

// adding a writable data descriptor - not configurable, not enumerable
setProp(4, myObj, 'myNumber', 25);

// adding a readonly data descriptor - not configurable, enumerable
setProp(1, myObj, 'myString', 'Hello world!');

// adding an accessor descriptor - not configurable, enumerable
setProp(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;
});

// adding a writable data descriptor (undefined value) - configurable, enumerable
setProp(7, myObj, 'myUndefined');

// adding an accessor descriptor (only getter) - configurable, enumerable
setProp(11, myObj, 'myDate', function() { return new Date(); });

// adding an accessor descriptor (only setter) - not configurable, not enumerable
setProp(8, myObj, 'myAlert', null, function(sTxt) { alert(sTxt); });

myObj.myAlert = myObj.myDate.toLocaleString() + '\n\n' + myObj.myString +
  '\nThe number ' + myObj.myNumber + ' represents the following bitmask: ' +
  myObj.myArray.join(', ') + '.';

// listing the enumerable properties
var sList = 'Here are the enumerable properties of myObj object:\n';
for (var sProp in myObj) {
  sList += '\nmyObj.' + sProp + ' => ' + myObj[sProp] + ';'
}

alert(sList);

Create a new non-native Object.setProperty() method

You can do the same thing with a descriptor object obtained through an anonymous constructor and an Object's custom method named setProperty():

JavaScript
// creating a new Object method named 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;
  };
})();

// creating a new empty object
var myObj = {};

// adding a writable data descriptor - not configurable, not enumerable
Object.setProperty(4, myObj, 'myNumber', 25);

// adding a readonly data descriptor - not configurable, enumerable
Object.setProperty(1, myObj, 'myString', 'Hello world!');

// etc. etc.
Note: The Object.setProperty() method could be also a proposal for a possible new JavaScript native method (see ECMAScript bug 335).

Syntax

JavaScript
Object.setProperty(<var>bitmask</var>, <var>obj</var>, <var>prop</var>[, <var>value/getter</var>[, <var>setter</var>]])

Parameters

bitmask
The descriptor bitmask (see below).
obj
The object on which to define the property.
prop
The name of the property to be defined or modified.
value/getter
Optional. The value to assign to a data descriptor or the getter function to assign to an accessor descriptor (depends on the bitmask).
setter
Optional. The setter function to assign to an accessor descriptor. If the flag 0x8 is setted to data descriptor this argument will be ignored.

Description

The non-native Object.setProperty() method works like the native Object.defineProperty() method, except for the descriptor object which is replaced with a descriptor bitmask. The bitmask argument has the following structure:

flag 0x1
The property is enumerable.
flag 0x2
The property is configurable.
flag 0x4
The property is writable.
flag 0x8
The property is an accessor descriptor.

So, the descriptor bitmask can have these possible numeric values:

  • 0: The bitmask represents a readonly data descriptor — not configurable, not enumerable (0000).
  • 1: The bitmask represents a readonly data descriptor — not configurable, enumerable (0001).
  • 2: The bitmask represents a readonly data descriptor — configurable, not enumerable (0010).
  • 3: The bitmask represents a readonly data descriptor — configurable, enumerable (0011).
  • 4: The bitmask represents a writable data descriptor — not configurable, not enumerable (0100).
  • 5: The bitmask represents a writable data descriptor — not configurable, enumerable (0101).
  • 6: The bitmask represents a writable data descriptor — configurable, not enumerable (0110).
  • 7: The bitmask represents a writable data descriptor — configurable, enumerable (0111).
  • 8: The bitmask represents an accessor descriptor — not configurable, not enumerable (1000).
  • 9: The bitmask represents an accessor descriptor — not configurable, enumerable (1001).
  • 10: The bitmask represents an accessor descriptor — configurable, not enumerable (1010).
  • 11: The bitmask represents an accessor descriptor — configurable, enumerable (1011).
Note: If the flag 0x8 is set to accessor descriptor the flag 0x4 (writable) will be ignored. If not, the setter argument will be ignored.

HTMLSelectElement.selectedIndex implementation

You can use the Object.defineProperty() method with native objects also. The following example shows how to implement the HTMLSelectElement's selectedIndex property in radio button groups.

HTML
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Radio group selectedIndex example</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
});

// try it!
function checkForm() {
  var nSelectedIndex = document.myForm.myRadioGroup.selectedIndex;
  if (nSelectedIndex < 0) {
    alert('Select a gadget!!');
    return false;
  }
  alert('Congratulations!! You selected the ' + document.myForm.myRadioGroup[nSelectedIndex].value + '.');
  return true;
}
</script>
</head>

<body>
  <form name="myForm" onsubmit="return(checkForm());">
    <fieldset><legend>Select a gadget</legend>
      <p><input type="radio" name="myRadioGroup" id="ourShirt" value="shirt" /> <label for="ourShirt">shirt</label><br />
      <input type="radio" name="myRadioGroup" id="ourPants" value="pants" /> <label for="ourPants">pants</label><br />
      <input type="radio" name="myRadioGroup" id="ourBelt" value="belt" /> <label for="ourBelt">belt</label><br />
      <input type="radio" name="myRadioGroup" id="ourShoes" value="shoes" /> <label for="ourShoes">shoes</label></p>
      <p><span style="cursor:pointer;text-decoration:underline;color:#0000ff;" onclick="document.myForm.myRadioGroup.selectedIndex=2;">Select our favorite gadget ;-)</span></p>
      <p><input type="submit" value="Order!" />
    </fieldset>
  </form>
</body>
</html>

License

© 2016 Mozilla Contributors
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-us/docs/web/javascript/reference/global_objects/object/defineproperty/additional_examples

Examples JavaScript Object