本页为Object.defineProperty()
提供一个附加示例。
如果你通过Object.defineProperty()
方法定义了许多属性,你可以通过二进制标志
使用相同的描述对象,一次次重新定义每个属性。
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);
Object.setProperty()
方法你可以对通过匿名构造函数和Object
的名为setProperty()
的自定义方法获取的描述符对象做同样的事情:
// 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.
Object.setProperty()
方法是一个 JavaScript 新的原生方法的提议(参见ECMAScript bug 335)。Object.setProperty(bitmask, obj, prop[, value/getter[, setter]])
bitmask
obj
prop
value/getter
setter
0x8
标识已经设置, 这个参数将会被忽略。除了用描述符位掩码替换的描述符对象外,非原生Object.setProperty()
方法与原生Object.defineProperty()
方法类似。 bitmask
参数具有以下结构:
0x1
0x2
0x4
0x8
所以,描述位掩码可以有以下可能的值:
0
: 位掩码表示只读数据描述符 — not configurable, not enumerable (0000
).1
: 位掩码表示只读数据描述符 — not configurable, enumerable (0001
).2
: 位掩码表示只读数据描述符 — configurable, not enumerable (0010
).3
: 位掩码表示只读数据描述符 — configurable, enumerable (0011
).4
: 位掩码表示可写数据描述符 — not configurable, not enumerable (0100
).5
: 位掩码表示可写数据描述符 — not configurable, enumerable (0101
).6
: T位掩码表示可写数据描述符 — configurable, not enumerable (0110
).7
: T位掩码表示可写数据描述符 — configurable, enumerable (0111
).8
: 位掩码表示可访问数据描述符 — not configurable, not enumerable (1000
).9
: 位掩码表示可访问数据描述符 — not configurable, enumerable (1001
).10
: 位掩码表示可访问数据描述符 — configurable, not enumerable (1010
).11
: 位掩码表示可访问数据描述符 — configurable, enumerable (1011
).0x8
被设置为访问描述符,则标志0x4
(可写)将被忽略。否则,setter
参数将被忽略。你也可以使用本地方法 Object.defineProperty()
。用以下例子展现如何实现HTMLSelectElement
和 selectedIndex
单选框组属性。
<!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>