Wednesday, August 15th, 2007

Lazy Function Definition Pattern

Category: Articles, JavaScript

Peter Michaux has written about the Lazy Function Definition Pattern.

His article takes you down the path of implementing a simple problem:

Write a function foo that returns a Date object that holds the time that foo was first called.

After critiquing 3 iterations he ends up with:

javascript

  1. var foo = function() {
  2.     var t = new Date();
  3.     foo = function() {
  4.         return t;
  5.     };
  6.     return foo();
  7. };

He then uses the technique to implement getScrollX, and to talk about how you can use defineGetter in certain browsers to simulate lazy definition for properties that aren’t functions.

Posted by Dion Almaer at 7:08 am
27 Comments

++---
2.7 rating from 64 votes

27 Comments »

Comments feed TrackBack URI

This function has changed my life

Comment by Michael — August 15, 2007

I dont get it, is this being sarcastic?

Comment by M. Stelling — August 15, 2007

nice try, but don’t rely on it too much, or you’ll get memory leaks via closures

Comment by anonymous — August 15, 2007

Why not use this?
var foo = function() {
var t = new Date();
return function() {
return t;
};
}();

Comment by Archer — August 15, 2007

Ugh. Had to read the article for this to make any sense at all.

Brilliant!

Whether or not I’ll ever use it…. tough to say

Comment by Marty — August 15, 2007

Oh, I made a mistake. My foo() only returns the Date object which is generated when the foo() is defined.

Comment by Archer — August 15, 2007

Not for nothing, but…
var foo=(function(){ return new Date(); })();
…executes the function once, no closures are involved, and the function itself falls out of scope after executed. Conditionals inside are more than fine. We use this all over Dojo (particularly in 0.9) for some of the exact reasons described, but we don’t create any unnecessary functions (though we probably do need to iterate over the codebase once we finally release 0.9 and make sure of that.)

Comment by Tom Trenka — August 15, 2007

btw, a better example for Ajaxian to post would have been the page scroll example; it clearly shows the reasoning behind such a technique. The example given above is not a good one, all it does is demonstrate using and creating functions on the fly.
Still, wrapping the entire thing in a function that is defined and executed once is probably the most efficient way of doing it. Taking a cue from one of the comments in the article:
var getScrollY=(function(){
if(window.pageYOffset) return function(){ return window.pageYOffset; };
if(document.documentElement.scrollTop) return function(){ return document.documentElement.scrollTop; };
if(document.body.scrollTop) return function(){ return document.body.scrollTop };
return function(){ return -1; }
})();
Once the outer function is executed, it falls out of scope and only the conditional met remains.

Comment by Tom Trenka — August 15, 2007

Hi Tom, In your first post your foo will hold a Date object for when foo was initially defined. The goal of the example in my article is to define a foo() that will return a Date object that corresponds to the first time foo() is _called_. In your second post the third conditional will not work because document.body does not exist when the script is loaded in the head element of the document. I discuss this in the article and this lack of information when a function is defined is the impetus for lazy definition altogether.

Comment by Peter Michaux — August 15, 2007

Thats awesome, I never knew you could redefine a function within itself.

Comment by Chris O'Brien — August 15, 2007

@anonymous: Closures do NOT cause memory leaks. Closures are a core part of JS, almost as foundational as mutable variables, and are integral to good JS programming. Every JS engine handles them with leaking. Memory leaks were caused in IE by circular references that included DOM elements, and these circular references were easily created with closures, but it wasn’t the fault of closures.
Function replacement is also used by AOP/function interposing style event handling, which I believe is the mechanism that Dojo uses for it’s connect function, and really love this style of event handling. A brilliant demonstration of the power of JS.
Peter, great job demonstrating another use of this power of the dynamic nature of JS.

Comment by Kris Zyp — August 15, 2007

@Peter: I’d suggest then that you make it a little clearer what your goal is–for instance, stating exactly what you said to me in that paragraph called “Warm up problem”. Too many people miss the distinction, particularly when it’s posted on Ajaxian first.

Also, you’re correct WRT document.body. It’s a bad sniff and the only reason I used it here was to illustrate the point, using an example someone posted as a comment to your post. Although IIRC, that branch would never be reached (since IE, which IIRC is the target of that sniff) will respond to the branch before it.

IMHO, WRT to the example, it’s the variable redefinition inside the body of the function itself that is questionable. But understandable.

Is there a reason why you don’t want to define something at load time (aside from waiting for a document to finish loading, at which point you could execute this onload)?

Comment by Tom Trenka — August 15, 2007

Tom,

IE will respond to document.documentElement or document.body branches depending on which mode it is in. Richard Cornford’s article that I reference explains in detail

http://www.jibbering.com/faq/faq_notes/not_browser_detect.html#bdScroll

If defining getScrollX/Y functions are delayed to window.onload then the user may interact with the page before the scroll functions are defined. Having them self-define on demand is a real advantage.

Comment by Peter Michaux — August 15, 2007

Peter,

This is useful information. I’m glad you’ve shared and explained it.

It’s particularly useful for object detection where you need to define functions for a particular browser. So, rather than check for say — document.attachEvent or document.addEventListener each time the addEvent method is called — you could just do it once and define the function.

Oliver

Comment by Oliver Tse — August 15, 2007

Very nice.

And, Peter, I can’t see how you could explain your goal better than you did.

Comment by Andrea — August 15, 2007

@Peter:
Granted (WRT the particular example), but again I think you’re picking a tree from the forest. I used that as an example because someone posted it as a comment on your site. It’s not an approach I would take for determining scroll position but everyone has different ways of doing things, and most of them are good solutions.

WRT to clarity, I learned the hard way that even graduate students benefit from points that are crystal clear. Even a small addition like “as opposed to defining this when the code is loaded” would be a benefit. I had no less than 3 pretty smart people take a look at this post (at Ajaxian) and then ask me “what’s the point of what he’s doing?”, even after reading your post. And obviously I missed part of your point as well (though admittedly I did not read it slowly enough).

The point I was trying to make in my second comment had nothing to do with scroll position and everything to do with pure JS; redefining a variable like that in something you’re executing has the potential for all sorts of side-effects, and while it’s a neat demonstration of some of the things JS is capable of, I would consider that to be a dangerous path to go down. You obviously use it correctly but like so many other things you can pull with it, it has an enormous potential for misuse and abuse–as is so often seen on the Web.

It reminds me of an article I read a while ago (think it was on Sitepoint but I’m not remembering off the top of my head) where someone was trying to demonstrate how to do “real OOP” through returning a function definition as the result of the new constructor. It works, and it demonstrates some of the real flexibility of the language (which is one of the reasons I love it) but it also demonstrates something that has a serious potential for abuse. We in the JS community have been seriously lax WRT to making it clear when and why we do tricky things, and that has a lot to do with the commentary I’ve been making.

Comment by Tom Trenka — August 15, 2007

Wouldn’t the following be slightly more efficient?

var foo = function() {
var t = new Date();
foo = function() {
return t;
};
return t;
};

In this way, the first time you call it, you wouldn’t need to call the redefined foo() again just to get the value of t?

Comment by Kevin Hoang Le — August 15, 2007

That risks too much confusion just to save an if. I think this is better:

var foo = function () {
var t;
return function () {
return t || (t = new Date());
};
}();

Comment by Douglas Crockforrd — August 15, 2007

My second observation is regarding the naming of the pattern in this article. I find it to be not lazy at all. In fact, what it suggests to do is always upfront instead of delaying, therefore it is the opposite of lazy.

Comment by Kevin Hoang Le — August 15, 2007

That risks too much confusion just to save an if. I think this is better:

var foo = function () {
var t;
return function () {
return t || (t = new Date());
};
}();

Are you kidding? That’s an attempt to reduce confusion? If your goal is to reduce confusion, why redefine the method you’re inside of?

Comment by Rufus — August 15, 2007

Douglas’ solution seems the most intuitive to me. I don’t like having the function’s name inside the function body, although you could do what another commenter did and use arguments.callee.

Regardless, this seems like a useful idiom. If you don’t like it or are confused by it, don’t use it.

Comment by Justin Kramer — August 15, 2007

Here’s a way to use it in real life:

function makeLazyDef(decideWhichFun) {
var f;
return function() {
if (f) {
return f();
}
f = decideWhichFun();
return f();
};
}
var test2 = makeLazyDef(function() {
var t = new Date();
// expensive if statements are here
alert('fun creation');
return function() {
return t;
}
});
var test3 = test2;
var test4 = makeLazyDef(function() {
alert('deciding what to use...');
if (typeof window.pageYOffset == 'number') {
return function() {
return window.pageYOffset;
};
} else if ((typeof document.compatMode == 'string') &&
(document.compatMode.indexOf('CSS') >= 0) &&
(document.documentElement) &&
(typeof document.documentElement.scrollTop == 'number')) {
return function() {
return document.documentElement.scrollTop;
};
} else if ((document.body) &&
(typeof document.body.scrollTop == 'number')) {
return function() {
return document.body.scrollTop;
}
} else {
return function() {
return NaN;
};
}
});
var test5 = test4;

Comment by Leo Lipelis — August 15, 2007

Kevin, the term “lazy” comes to us from the real world, where this pattern has been in widespread use for decades and is called “lazy caching”.

Comment by Trav — August 16, 2007

Rethinking, the only problem with your solution is that we would be filling the memory with variables that would never be released, just because of the closures.

*1# Case*

Suppose “foo” should return a static value, result of an intensive and complex code.

Using the proposed pattern, we would have something like this:


var foo = function() {
var result ;
var a,b,c,d,e ;
// using a,b,c,d,e to fill return
foo = function() {
return result;
};
return foo();
};

In the above example, “a,b,c,d,e” will never get released just because of our closure, even if not anymore needed. We just need “result”.

I was tempted to mix your solution with Jona’s proposal, which seams to bring better results regarding memory usage:


var foo = function()
{
return arguments.callee.t || (arguments.callee.t = (function(){
var result ;
var a,b,c,d,e ;
// using a,b,c,d,e to fill return
return result ;
})());
};

But, in the above example, the calculation will run for all calls if the final “result” is null, false, 0, NaN, etc. So, this is not the definitive way to go.

After further thoughts, I’ve also considered that, the “return a || b” approach, internally, has the same value (and possibly the same performance) as “return a ? a : b”, because in both cases the compiler will have to check the value of “a” before returning it (it means that it is not true that “a” is “immediately” returned in the first case). So, this is not a real shortcut.

So, theoretically, the following could be a definitive solution for this case:


var foo = function()
{
if ( typeof arguments.callee.t != 'undefined' )
return arguments.callee.t ;

var result ;
var a,b,c,d,e ;
// using a,b,c,d,e to fill return
return ( arguments.callee.t = result ) ;
};

Which means that we are back to the “Ancient Technology”, not opting to use the confusing “Module Pattern”, considering that “Functions are Objects”, but considering also that arguments.callee.t could be any kind of “defined” valued.

*2# Case*

Supposing instead the “foo” should return a value that uses different algorithms, chosen after a complex calculation.

Here we have the getScrollY example again, which will have the same memory allocation issue explained in the previous case.

Just to exemplify, suppose we code in the following way:


var getScrollY = function() {

if (typeof window.pageYOffset == 'number') {

return (getScrollY = function() {
return window.pageYOffset;
})();
}

var compatMode = document.compatMode;
var documentElement = document.documentElement;

if ((typeof compatMode == 'string') &&
(compatMode.indexOf('CSS') >= 0) &&
(documentElement) &&
(typeof documentElement.scrollTop == 'number')) {

return (getScrollY = function() {
return documentElement.scrollTop;
})();

}

var body = document.body ;
if ((body) &&
(typeof body.scrollTop == 'number')) {

return (getScrollY = function() {
return body.scrollTop;
})();

}

return (getScrollY = function() {
return NaN;
})();
};

In the above case, we have a complete chaos, because “compatMode”, “documentElement” and “body” will be part of the closure scope forever, even if not needed in all cases (and potentially leaking).

In this case, a sane proposal, achieving the performance enhancements aimed by your pattern, would be using an old way of avoiding closures, by defining all functions outside “foo”:


var getScrollY = function() {

if (typeof window.pageYOffset == 'number')
return (getScrollY = getScrollY.case1)();

var compatMode = document.compatMode;
var documentElement = document.documentElement;

if ((typeof compatMode == 'string') &&
(compatMode.indexOf('CSS') >= 0) &&
(documentElement) &&
(typeof documentElement.scrollTop == 'number'))
return (getScrollY = getScrollY.case2)();

var body = document.body ;
if ((body) &&
(typeof body.scrollTop == 'number'))
return (getScrollY = getScrollY.case3)();

return (getScrollY = getScrollY.case4)();
};

getScrollY.case1 = function() {
return window.pageYOffset;
};

getScrollY.case2 = function() {
return documentElement.scrollTop;
};

getScrollY.case3 = function() {
return body.scrollTop;
};

getScrollY.case4 = function() {
return NaN;
};

Here again, we have complete freedom in the getScrollY main calculations, avoiding closures. By using the “Functions are Objects” approach, just one of the “case1”, “case2”, “case3” and “case4” references will remain after the first call to getScrollY, freeing unneeded memory allocation too.

Conclusion
—-
Your pattern is interesting, but must be very well controlled while coding it. The above two cases are examples where it would work, but would not perform well regarding memory management (essentially because of the closures), and the proposed patterns could give better results, achieving similar performance enhancements.

Ps.: I’ve written all the code in this textarea… so, please don’t give much attention to possible typos.

Comment by FredCK — August 17, 2007

@Tom
You’re probably right but your example uses a “one-time function” while, in my opinion, if You don’t need a closure it’s quite a non-sense.

var u,getScrollY=
window.pageYOffset!==u?function(){return window.pageYOffset}:
document.documentElement.scrollTop!==u?function(){return document.documentElement.scrollTop}:
document.body.scrollTop!==u?function(){return document.body.scrollTop}:
function(){return -1};

same result, any extra function creation or execution.

I suppose if You’re looking for performances the best way is to assign directly correct function (depending on browser) and not switching browser on every function call:

var doStuff = function(){if(IE)doIEStuff();else doFFStuff();};

// a better way (performances)
var doStuff = IE ? doIEStuf : doFFStuf;

In this case You don’t need to do that:
var doStuff = (function(){return IE ? doIEStuf : doFFStuf})();

do You agree ?

At the same time example showed in this post is really “strange” and uses two scopes to do one simple thing … so the goal is not so clear and I can’t understand why getScrollY example should require at least one extra function creation and one call before its assignment.

That’s why I think timing example is even better than getScrollY one :-)

Comment by Andrea Giammarchi — August 17, 2007


var foo = function() {
return arguments.callee.timestamp = (arguments.callee.timestamp || new Date());
};

Comment by Andrew Rev — August 20, 2007

function lazy(base, key, fn) {
base[key] = function(){
return (this[key] = Constant(fn.apply(this, arguments)))();
};
};

lazy.Constant = function(value) { return function() { return value } };

lazy(this, 'foo', function(){
return + new Date;
});

Comment by henrah — February 16, 2008

Leave a comment

You must be logged in to post a comment.