Have a look at this..
function getTemplate(id){
var scripts = document.getElementsByTagName("script");
for(script in scripts){
var sid = script.getAttribute(id);
if(id==sid){
return script.innerHtml;
}
}
}
Fairly simple function that loads the contents of a specific script tag (based on id) so that it can be used as a template (for something). The reasons why you would do this is not important but the implementation of this function is. Every time you call this function you are re-selecting all the tag elements on the page which is just plain wrong. A quick fix for this would be to cache the results after the initial call and use the cache.
var cache = document.getElementsByTagName("script");
function getTemplate(id){
for(script in cache){
var sid = script.getAttribute(id);
if(id==sid){
return script.innerHtml;
}
}
}
This would allow the the costly getElementsByTagName to be called once and the results reused. This however starts polluting the global namespace with unnecessary variables plus even if the getTemplate function is never used you still have the cost of one call to getElementsByTagName which is pretty poor. You can get around these issues fairly easily by extending the actual function object itself
function getTemplate(id){
if(!getTemplate.cache){
getTemplate.cache = document.getElementsByTagName("script");
}
for(script in getTemplate.cache){
var sid = script.getAttribute(id);
if(id==sid){
return script.innerHtml;
}
}
}
The benefits of this approach are that you aren't polluting the global namespace and you are only making the call to getElementsByTagName ONLY when it is needed but it does still have an issue. The cache should only be available to the getTemplate functions internal logic but it is currently exposed allowing other functions to remove or update it.
Introducing the Lazy Function Definition Pattern. Thanks to the dynamic nature of JavaScript functions can actually redefine themselves at runtime and this allows us to provide private, lazy, one time processing without polluting the global namespace. Lets see it in action...
function getTemplate(id){
var cache = document.getElementsByTagName("script");
getTemplate = function(id){
for(script in cache){
var sid = script.getAttribute(id);
if(id==sid){
return script.innerHtml;
}
}
}
return getTemplate(id);
}
The big change here is the middle statement. getTemplate is actually redefining itself. So what happens here?
- On the first call getElementsByTagName is called and stored in the cache variable
- The function then redefines itself as simple function that simply loops through the already defined cache
- The function then calls itself (the new self!)
Thanks to the creation of a closure the redefined function actually has access to the variables of the old definition - cache. This means that cache is private to the new function, only called once and not part of the global namespace. Exactly what I wanted. Programatically as well it's a better solution than the ones above because there are no checks to see if cache is defined saving a few cycles each call.