1) First Javascripts ability for functions to take typeless objects/parameters.
Example:
function example( obj ) {} //obj can contain any type of members and has no defined type function example( a, b ) {} //a or b can be objects, strings, numbers etc.
The problem here is knowing what to expect for a function, either the type or what variables are expected to be inside an object. Half of this problem could be solved through brute force. You could name the variables passed in. For instance, addTwoNumbers( firstNumber, secondNumber ). Somone still may call that function with a string when you are expecting a number. When objects are passed in to a function it is extremely hard to know what needs to be in the object without poking around in the function and this is very good way of introducing problems with your code!
2) Secondly Javascripts ability for functions to take any range of parameters.
Example:
function example( a, b ) {} //can be called with example(1) or example( 1,2,3)
Typlessness is great, but sometimes you'd like to make sure you know what you are getting!
Let's talk about the problem where people call your function with too few parameters and you are expecting all of them.
Example:
Your function is: addTwoNumbers( first, second )
Someone calls it instead with: addTwoNumbers( "5" );
Well you could solve this problem fairly easily by adding a bunch of conditional tests at the front of the function. For instance do this:
function addTwoNumbers( first, second, .., .., .. ) { if ( typeof first === "undefined" or typeof second === "undefined" ... ) { throw "fail" } }
This gets tedious very fast! Not only that but you don't really know if they pass in a string or number without further testing.
Introducing Contracts!
What if I told you that you could solve both problems at once and make your code much easier to read and understand! Well... you can with contracts!
A contract in this article is a template which describes how data must be structured. Contracts will be used to test the object passed in by a one line piece of code. It will also indicate what the function expects from what is passed in.
Example of a contract
Say we have a chart that you can add data to but you must provide a name and y coordinate. The function you might write will look like this:
function addChartPoint( chartPoint ) { if ( !Verify( chartPoint, { Name: "", Y: 0 } ) { throw "missing data!"; chartPoints.push( chartPoint ); }
In this example, { Name: "", Y: 0} is the contract. In the verify() function it will check if chartPoint has the members Name and Y and then if Name is a string and Y is a number. Now you can see this makes it very easy to check the validity of your parameters AND let others know what is expected of what is passed in. You don't have to tediously write out checks for each item, how about that?!
So you are probably wondering what magic is in verify function. Well Javascript is so flexible that we can compare both objects by iterating keys to determine if they match or not to create a nice verify function.
Here is an example of a verify function. (Note this verify function does not check type in this example, just for names)
var verify = function( dataItem, template ) { var itemsToFill = Object.keys(template); for ( var index = 0; index < itemsToFill.length; index++ ) { var keyName = itemsToFill[index]; var isObject = typeof template[keyName] === "object"; if( typeof dataItem[keyName] === 'undefined') { //one of the expected types was not found! return false; } else if ( template[keyName].constructor === Array ) { for ( var item = 0; item < dataItem[keyName].length; item++ ) { if ( !verify( template[keyName][0], (dataItem[keyName])[item] ) ) { return false; } } } else if ( isObject ) { if ( !verify( template[keyName], dataItem[keyName] ) ) { return false; } } } return true; }; exports.verify = verify;
As you can see this can be very powerful in making your functions easier to read and also test validity of parameters getting passed in! The code above also supports very complex templates but that is where the power lies! Try this one out for example:
function calculatePortfolioBalance( portfolio ) { if ( !Verify( portfolio, { days: [ { stocklist: "", holding: { shares: 0, percent: 0}}] ) throw "bad object passed"; }
This will make sure that we have a days array and that each days[0...n] contains a stocklist variable, holding object with children shares and percent. Pretty wild eh! This concept is even more powerful when you use it in conjunction with class declarations as well!
No comments:
Post a Comment