webdev: (Default)
[personal profile] webdev
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:
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 modified
strPropName: The name of the property being created or modified
objDescriptors: An object that provides a map of descriptors in the form of key/value pairs

Valid 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 view
strPropName: 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.

Profile

webdev: (Default)
Jon Reid

October 2013

S M T W T F S
  12 345
6789101112
13141516171819
20212223242526
272829 3031  

Links