Creating Object Properties {the dangling participle of JS Objects}

This is a series of posts that explains some of the fundamental rules for developing complex JavaScript applications.

As most of us know, well I would hope most of us that is, properties in objects have some fundamental flaws in them, that, like many situations in JavaScript our bits and bites are left hanging out!  Object properties that are exposed publicly, whether in a module pattern or an instance have absolutely zero control over them.

If one does some research however you will dig up the Object.defineProperty method and supporting methods that as of IE9 are now common across all modern browsers.  If you are interested in reading up on the defineProperty Object method I have found the best documentation to be over at Mozilla . While Object.defineProperty does provide us enhancements such as read only, freezing and preventing deletion is certainly does not, in my opinion, provide much power. A property should provide the following:

  • Verbose setters and getter methods,
  • Conditional checking on setters,
  • Chaining of base object setters,
  • Watches and events.

So let’s go through each of these concepts. Verbose setter and getter methods are in essence methods to set and get the property value while protected the actually base value. So assuming we have an object entitled myLittleObject with a property called height I should be able to say myLittleObject.height.set(10) or myLittleObject.height.get(). Now a very good argument can be made for simple having a single method on height which either returns the value or takes a value, thusly myLittleObject.height(10) sets the value to 10 and myLittleObject.height() returns 10. While I have nothing against this, I simply prefer my code to be extremely verbose, hence my preference for property.set/get.  It also helps that I am an extremely fast typist, so perhaps that helps!

Conditional Checking of setter refers to the ability to apply a test to value being assigned to a property. One of the general weaknesses of JavaScript is its’ base data types. Let’s face it Number() really does not cut it for numeric values. Most languages, in fact I cannot think of any that do not support Int, Long, Double etc. I recognize that there is argument that there is no point to creating other base types as they will all be based against the browser engine data type so there is no memory or speed enhancement. However from a pure programmatic point of view being able to apply tests against a value being passed in provides for much greater control.

Chaining is a key part of todays’ JavaScript frameworks. Chaining is the process of returning the base object on calling the setter method of that object’s property. For example, if we have an object with two properties we should be able to do, myObject.x.set(1).y.set(2) rather than having to make two separate statements on myObject. In this case the set method of each property returns myObject.

Watches and events are an extremely important aspect of a property object. Many years ago Mozilla introduced watches on variables which in essence allowed you to create binders to the watch to ‘watch’ for certain conditions.  This could be viewed at its core level as an event style paradigm. For the purpose of this blog post I will not cover creating the watch/event object as that is beyond the scope of this post. I will however discuss the creation of an event object in the next post.

Now that we have gone through the requirements let’s look at the basics for the method.

/**
  * @method createProp
  * @description Creations a property on the passed object and property based on the provided config.
  * @param scope [object] The location within the object to create property
  * @param prop [string] The name of the property to create
  * @param config [object] Mixed object declaring the details of the property, see defaults.
  * @return null
  **/
 createProperty = function (scope,prop,config){
                 var defaults = {
                                 // @prop verbose (bool) When true properties are given declaritive set and get methods rather
                                 // than a single method that tests for the existence of a passed value.
                                 verbose : false,

                                 //@prop writeable (bool) indicates if the property will have a setter method
                                 writeable : true,

                                 //@prop value (mixed) value of the property
                                 value : null,

                                 //@prop allowNull (bool) When true property may be null
                                 allowNull : true,

                                 //@prop allowUndefined (bool) When true property may be undefined
                                 allowUndefined : false,

                                 //@prop valueCondition (function) A function passed the value being provided to the setter method. If false is returned  the property value will not be changed
                                 valueCondition:function(){return true},

                                 // @prop setterErrorThrow (object) Object containing booleans on whether to throw errors on setter failuers. For example if  the config property allowNull is false have a setterErrorThrow of null true will throw an error when the property is attempting to be set to null. If the properties root sibling is true then the sibling throw property will be ignored.
                                 setterErrorThrow : {
                                                 allowNull : false,
                                                 allowUndefined : false,
                                                 valueCondition : false
                                 },

                                 // @prop chain (bool) When true the scope object is returned on the setter method allowing for the chaining of objects 
                                 chain : true,

                                 // @prop scope (mixed) object to return on setter when chain is true, defaults to passed scope on function
                                 scope : scope
                 };
                 config = applyIf(config,defaults);
 };

If we remember back to my previous post ‘Writing Flexible JavaScript Functions’ I introduced the applyIf method that allows for the assignment of properties in an object. As can be seen by the above code I have set up a set of default properties along with explanations of what each will do.

Now that we have this set up lets write the actual code to return the object following the applyIf line.

var propObject = (function(config){
                 var currentValue=config.value,
                 propConfig = config,
                 methods = {
                                 get : function(){
                                                 return currentValue;
                                 }
                 };
                 if(propConfig.writeable){
                                 methods.set = function(value){
                                                 if(value === undefined && !propConfig.allowUndefined){
                                                                 if(propConfig.setterErrorThrow.allowUndefined){
                                                                                 throw('undefined not allowed');
                                                                 }
                                                 }else if(value === null && !propConfig.allowNull){
                                                                 if(propConfig.setterErrorThrow.allowNull){
                                                                                 throw('null not allowed');
                                                                 }              
                                                 }else if(!propConfig.valueCondition(value)){
                                                                 if(propConfig.setterErrorThrow.valueCondition){
                                                                                 throw('value condition fail');
                                                                 }                                                                              
                                                 }else{
                                                                 currentValue=value;
                                                 };
                                                 return propConfig.chain?propConfig.scope:value;
                                 }
                 }
                 return methods;
 }(config));

 if(config.verbose){
                 scope[prop]=Object.freeze(propObject);
 }else{
                 scope[prop]=Object.freeze(function(value){
                                 if(value!==undefined && propObject.set!==undefined){
                                                 return propObject.set(value);
                                 }else if(value===undefined){
                                                 return propObject.get();
                                 }
                 })
 }

As you can see this is a fairly simple implementation. I create an internal object called propObject with the required methods along with a copy of the passed config. Once the object is created I check the config.verbose setting to decide how to assign the object to the passed scope and property.

The first thing that you will most likely be thinking at this moment is why not use the prototype model as most likely we would have a fair number of these properties created.  You would of course be 100% correct; using the prototype model would be more efficient.  But herein lies the key issue with the prototype model. Any prototyped method can only use the this keyword to refer to the instance on the object that is calling the method.  This being the cased how do you get the protected value since it is not exposed within the object.  To use the prototype model you would have to expose the value within the instance which defeats the very purpose of creating setters and getters in the first place.  Although I love the prototype patter I see the issue of closures on the instance being the most significant drawback to the implementation.

Over the past several posts I have introduced some key ideas for design patters, function implementations etc. In my next post I will be putting them into a framework entitled mf.  I choose mf not to reflect Mr. Purple and his Sexy Mf but rather it stands for my framework.  Although in times of frustration one could use the other definition! Once we have the basic framework set out I will be introducing how to write your own event component followed by some other key components you need to have in a custom framework.

Until then…cheers

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s