На этой странице представлены дополнительные примеры использования метода 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>