MDN’s new design is in Beta! A sneak peek: https://blog.mozilla.org/opendesign/mdns-new-design-beta/

Дополнительные примеры по Object.defineProperty

На этой странице представлены дополнительные примеры использования метода Object.defineProperty().

Использование бинарных флагов вместо объекта дескриптора свойства

Если вы хотите определить множество свойств через метод Object.defineProperty(), вы можете использовать один и тот же объект дескриптора для каждого свойства, переопределяя его время от времени через бинарные флаги.

var oDesc = {};
function setProp (nMask, oObj, sKey, vVal_fGet, fSet) {
  if (nMask & 8) {
    // дескриптор доступа
    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 {
    // дескриптор данных
    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 является битовой маской:
*  флаг 0x1: свойство является перечисляемым,
*  флаг 0x2: свойство является настраиваемым,
*  флаг 0x4: свойство является записываемым,
*  флаг 0x8: свойство является дескриптором доступа.
* oObj - объект, на котором определяется свойство;
* sKey - имя определяемого или изменяемого свойства;
* vVal_fGet - значение, присваиваемое дескриптору данных, либо функция геттера, присваиваемая дескриптору доступа (в зависимости от битовой маски);
* fSet - функция сеттера, присваиваемая дескриптору доступа;
*
* Возможные значения битовой маски:
*
*  0  : дескриптор данных только для чтения - не настраиваемый, не перечисляемый (0000).
*  1  : дескриптор данных только для чтения - не настраиваемый, перечисляемый (0001).
*  2  : дескриптор данных только для чтения - настраиваемый, не перечисляемый (0010).
*  3  : дескриптор данных только для чтения - настраиваемый, перечисляемый (0011).
*  4  : записываемый дескриптор данных - не настраиваемый, не перечисляемый (0100).
*  5  : записываемый дескриптор данных - не настраиваемый, перечисляемый (0101).
*  6  : записываемый дескриптор данных - настраиваемый, не перечисляемый (0110).
*  7  : записываемый дескриптор данных - настраиваемый, перечисляемый (0111).
*  8  : дескриптор доступа - не настраиваемый, не перечисляемый (1000).
*  9  : дескриптор доступа - не настраиваемый, перечисляемый (1001).
*  10 : дескриптор доступа - настраиваемый, не перечисляемый (1010).
*  11 : дескриптор доступа - настраиваемый, перечисляемый (1011).
*
*  Обратите внимание: если установлен флаг 0x8 (дескриптор доступа), флаг 0x4 (записываемый)
*  будет проигнорирован. Если же нет, будет проигнорирован аргумент fSet.
*/

// создаём новый пустой объект
var myObj = {};

// добавляем записываемый дескриптор данных - не настраиваемый, не перечисляемый
setProp(4, myObj, 'myNumber', 25);

// добавляем дескриптор данных только для чтения - не настраиваемый, перечисляемый
setProp(1, myObj, 'myString', 'Привет, мир!');

// добавляем дескриптор доступа - не настраиваемый, перечисляемый
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;
});

// добавляем записываемый дескриптор данных (со значением undefined) - настраиваемый, перечисляемый
setProp(7, myObj, 'myUndefined');

// добавляем дескриптор доступа (только геттер) - настраиваемый, перечисляемый
setProp(11, myObj, 'myDate', function() { return new Date(); });

// добавляем дескриптор доступа (только сеттер) - не настраиваемый, не перечисляемый
setProp(8, myObj, 'myAlert', null, function(sTxt) { alert(sTxt); });

myObj.myAlert = myObj.myDate.toLocaleString() + '\n\n' + myObj.myString +
  '\nЧисло ' + myObj.myNumber + ' представляет следующую битовую маску: ' +
  myObj.myArray.join(', ') + '.';

// выводим все перечисляемые свойства
var sList = 'Перечисляемые свойства объекта myObj:\n';
for (var sProp in myObj) {
  sList += '\nmyObj.' + sProp + ' => ' + myObj[sProp] + ';'
}

alert(sList);

Создание нового неродного метода Object.setProperty()

Вы можете проделать ту же вещь с дескриптором объекта, полученным из анонимного конструктора и пользовательский метод setProperty() объекта Object:

// создаём в объекте Object новый метод Object.setProperty()

new (function() {
  var oDesc = this;
  Object.setProperty = function(nMask, oObj, sKey, vVal_fGet, fSet) {
    if (nMask & 8) {
      // дескриптор доступа
      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 {
      // дескриптор данных
      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;
  };
})();

// создаём новый пустой объект
var myObj = {};

// добавляем записываемый дескриптор данных - не настраиваемый, не перечисляемый
Object.setProperty(4, myObj, 'myNumber', 25);

// добавляем дескриптор данных только для чтения - не настраиваемый, перечисляемый
Object.setProperty(1, myObj, 'myString', 'Привет, мир!');

// и т.д.
Обратите внимание: Метод Object.setProperty() также предложен в качестве возможного нового родного метода JavaScript (смотрите баг ECMAScript номер 335).

Синтаксис

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

Параметры

bitmask
Дескриптор битовой маски (смотрите ниже).
obj
Объект, на котором определяется свойство.
prop
Имя определяемого или изменяемого свойства.
value/getter
Необязательный параметр. Значение, присваиваемое дескриптору данных, либо функция геттера, присваиваемая дескриптору доступа (в зависимости от битовой маски).
setter
Необязательный параметр. Функция сеттера, присваиваемая дескриптору доступа. Если установлен флаг 0x8 (дескриптор данных), этот аргумент будет проигнорирован.

Описание

Не родной метод Object.setProperty() работает подобно родному методу Object.defineProperty(), за исключением того, что объект дескриптора заменён битовой маской дескриптора. Аргумент bitmask имеет следующую структуру:

флаг 0x1
Cвойство является перечисляемым.
флаг 0x2
Cвойство является настраиваемым.
флаг 0x4
Cвойство является записываемым.
флаг 0x8
Cвойство является дескриптором доступа.

Таким образом, битовая маска дескриптора может принимать следующие числовые значения:

  • 0: Битовая маска представляет дескриптор данных только для чтения — не настраиваемый, не перечисляемый (0000).
  • 1: Битовая маска представляет дескриптор данных только для чтения — не настраиваемый, перечисляемый (0001).
  • 2: Битовая маска представляет дескриптор данных только для чтения — настраиваемый, не перечисляемый (0010).
  • 3: Битовая маска представляет дескриптор данных только для чтения — настраиваемый, перечисляемый (0011).
  • 4: Битовая маска представляет записываемый дескриптор данных — не настраиваемый, не перечисляемый (0100).
  • 5: Битовая маска представляет записываемый дескриптор данных — не настраиваемый, перечисляемый (0101).
  • 6: Битовая маска представляет записываемый дескриптор данных — настраиваемый, не перечисляемый (0110).
  • 7: Битовая маска представляет записываемый дескриптор данных — настраиваемый, перечисляемый (0111).
  • 8: Битовая маска представляет дескриптор доступа — не настраиваемый, не перечисляемый (1000).
  • 9: Битовая маска представляет дескриптор доступа — не настраиваемый, перечисляемый (1001).
  • 10: Битовая маска представляет дескриптор доступа — настраиваемый, не перечисляемый (1010).
  • 11: Битовая маска представляет дескриптор доступа — настраиваемый, перечисляемый (1011).
Обратите внимание: Если установлен флаг 0x8 (дескриптор доступа), флаг 0x4 (записываемый) будет проигнорирован. Если же нет, будет проигнорирован аргумент setter.

Реализация HTMLSelectElement.selectedIndex

Также вы можете использовать метод Object.defineProperty() с родными объектами. Следующий пример показывает, как реализовать свойство selectedIndex элемента HTMLSelectElement для группы радиокнопок.

<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Пример реализации свойства selectedIndex для группы радиокнопок</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
});

// Попробуем!
function checkForm() {
  var nSelectedIndex = document.myForm.myRadioGroup.selectedIndex;
  if (nSelectedIndex < 0) {
    alert('Выберите гаджет!!');
    return false;
  }
  alert('Поздравляем!! Вы выбрали ' + document.myForm.myRadioGroup[nSelectedIndex].value + '.');
  return true;
}
</script>
</head>

<body>
  <form name="myForm" onsubmit="return(checkForm());">
    <fieldset><legend>Выберите гаджет</legend>
      <p><input type="radio" name="myRadioGroup" id="ourShirt" value="рубашка" /> <label for="ourShirt">рубашка</label><br />
      <input type="radio" name="myRadioGroup" id="ourPants" value="брюки" /> <label for="ourPants">брюки</label><br />
      <input type="radio" name="myRadioGroup" id="ourBelt" value="пояс" /> <label for="ourBelt">пояс</label><br />
      <input type="radio" name="myRadioGroup" id="ourShoes" value="обувь" /> <label for="ourShoes">обувь</label></p>
      <p><span style="cursor: pointer; text-decoration: underline; color: #0000ff;" onclick="document.myForm.myRadioGroup.selectedIndex=2;">Выберите ваш любимый гаджет ;-)</span></p>
      <p><input type="submit" value="Купить!" />
    </fieldset>
  </form>
</body>
</html>

Метки документа и участники

Метки: 
 Внесли вклад в эту страницу: Mingun
 Обновлялась последний раз: Mingun,