JavaScript Property Descriptors
Apr. 13th, 2013 04:11 pm![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
We're all familiar with properties on objects. In JavaScript, an object with its properties can be defined in several different ways. Using literal notation, for example:
With the advent of ECMAScript 5, every property has a set of descriptors defined upon it. These descriptors specify how (or whether) the property is accessed. You can, for example, specify whether or not a property should be enumerable in a for-in loop, or whether or not a property can be changed later on.
ECMAScript 5 specifies a new set of methods on the global
Valid descriptors for a simple property are:
So in our example above, we would do the following:
Of course those are the default values for those descriptors, so you don't need to define them explicitly. I'm showing them here to illustrate the use of the new method.
In addition, ECMAScript 5 specifies a new kind of property called an Accessor. Accessor properties cannot have
Accessor properties are super-powerful and I'll have to write another blog post about them sometime--you can use them to create data binding!
You can also set the descriptors on properties added when you use
Finally, to access the descriptors on an existing property, you can use the
The method returns a descriptor map object, just like the ones defined in the above examples.
var myObject = { myProperty: "hello world" } console.log(myObject.myProperty); // will output "hello world" to the console.
With the advent of ECMAScript 5, every property has a set of descriptors defined upon it. These descriptors specify how (or whether) the property is accessed. You can, for example, specify whether or not a property should be enumerable in a for-in loop, or whether or not a property can be changed later on.
ECMAScript 5 specifies a new set of methods on the global
Object
object for accessing property descriptors. To set a descriptor on an object's properties, you have to use the new Object.defineProperty
method, which takes the following parameters:targetObject
: The object that is being modifiedstrPropName
: The name of the property being created or modifiedobjDescriptors
: An object that provides a map of descriptors in the form of key/value pairsValid descriptors for a simple property are:
configurable
: when set to false, you cannot change the property's value or any of its descriptors. Defaults to true.enumerable
: when set to true (the default) the property will be included as a property on a for-in loop executed on the object.writable
: when set to false, you cannot change the property's value. Defaults to true.value
: the actual value of the property.So in our example above, we would do the following:
var myObject = {}; Object.defineProperty(myObject, "myProperty", { value: "hello world", enumerable: true, writable: true, configurable: true }); console.log(myObject.myProperty); // will output "hello world" to the console.
Of course those are the default values for those descriptors, so you don't need to define them explicitly. I'm showing them here to illustrate the use of the new method.
In addition, ECMAScript 5 specifies a new kind of property called an Accessor. Accessor properties cannot have
value
or writable
descriptors and instead have get
and set
descriptors which are functions that are invoked when the property is accessed.var myObject = { _myProperty = '' }; Object.defineProperty(myObject, "myProperty", { get: function() { console.log('myObject.myProperty was accessed'); return this._myProperty; }, set: function(value) { console.log('myObject.myProperty was changed'); this._myProperty = value; }, enumerable: true, configurable: true }); var testVar = myObject.myProperty; // will output "myObject.myProperty was accessed" to the console. myObject.myProperty = true; // will output "myObject.myProperty was changed" to the console.
Accessor properties are super-powerful and I'll have to write another blog post about them sometime--you can use them to create data binding!
You can also set the descriptors on properties added when you use
Object.create
, which (just like Object.defineProperty
) also takes an optional descriptor map:var templateObject = { templateProperty: "hello from the template", templateMethod: function() { console.log(this.templateProperty); } } var myNewObject = Object.create(templateObject, { // Notation convention: A property prefixed with _ is "private." _myVal: { enumerable: false, value: null }, // Define getter and setter and use the "private" property to store the value. myVal: { get: function() { return this._myVal; }, set: function(newVal) { if (newVal !== this._myVal) { this._myVal = newVal; } }, enumerable: true }, // Here's how to create a method. newMethod: { value: function() { console.log('Hello from the new properties.'); }, enumerable: false } }); myNewObject.newMethod(); // will output 'Hello from the new properties.' to the console. myNewObject.templateMethod(); // will output 'hello from the template' to the console. myNewObject.myVal = 5; // Will use set() method. console.log(myNewObject.myVal); // will use get() method to output 5 to console.
Finally, to access the descriptors on an existing property, you can use the
Object.getOwnPropertyDescriptor
method, which takes two parameters:targetObject
: The target object with the property whose descriptors you want to viewstrPropName
: The name of the property whose descriptors you want to view.The method returns a descriptor map object, just like the ones defined in the above examples.