There are a lot of curry functions examples out there so I decided I’d share mine as well. The code was inspired by a blog posting from Dustin Diaz who was in turn inspired by one from Dan Webb. Those posts can probably do an overall better job of explaining currying than I can so I’m not going to go in to the idea of currying in any great detail. So I’ll just say the currying pattern is useful for attaching data to a function in a way that avoids using global variables and simplifies the use of closure.
When people first start programming in JavaScript they often use global variables to save data for later function calls like this:
Global Demo
<script type="text/javascript">
var global_target;
var global_oldText;
function doItGlobal(e) {
e = e || window.event;
global_target = e.target || e.srcElement;
global_oldText = global_target.innerHTML;
global_target.innerHTML = "Click!";
setTimeout(function () {
global_target.innerHTML = global_oldText;
}, 1000);
return (false);
}
</script>
<a href="#" onclick="return doItGlobal(event);">Global test</a>
<a href="#" onclick="return doItGlobal(event);">Test 2</a>
<a href="#" onclick="return doItGlobal(event);">Test 3</a>
Here you see a couple global variables are used to save the link that was clicked and the old text that was inside. When clicking just one link at a time it works, but if you click more than one (in the one second time frame) the next click corrupts the global variables and the first link won’t reset to its old text correctly.
One way to solve this problem is using closure instead of global variables like this:
Closure Demo
<script type="text/javascript">
function doItClosure(e) {
e = e || window.event;
var target = e.target || e.srcElement;
var oldText = target.innerHTML;
target.innerHTML = "Click!";
setTimeout(function () {
target.innerHTML = oldText;
}, 1000);
return (false);
}
</script>
<a href="#" onclick="return doItClosure(event);">Closure test</a>
<a href="#" onclick="return doItClosure(event);">Closure test 2</a>
<a href="#" onclick="return doItClosure(event);">Closure test 3</a>
Closure test Closure test 2 Closure test 3
This saves a unique copy of the data for each call and solves the previous problem. Often this is a very clear and concise way to handle the problem. But other times, usually when functions start getting more complex with multiple callbacks for multiple events, it can be hard to follow the trail of closure to see exactly which variables are attached to which internal functions. That is when I find the curry pattern like this very useful:
Curry Demo
<script type="text/javascript">
function doItCurry(e) {
e = e || window.event;
var target = e.target || e.srcElement;
var oldText = target.innerHTML;
target.innerHTML = "Click!";
setTimeout(exfer.curry(target, function (text) {
this.innerHTML = text;
}, oldText), 1000);
return (false);
}
</script>
<a href="#" onclick="return doItCurry(event);">Curry test</a>
<a href="#" onclick="return doItCurry(event);">Curry test 2</a>
<a href="#" onclick="return doItCurry(event);">Curry test 3</a>
Curry test Curry test 2 Curry test 3
Once you get used to the pattern you can see clearly that target and oldText are passed in to time timeout function callback where they are operated upon. Internally the curry function also uses closure but it is hidden from the event handler. Also a curry function can be used to assign scope to the “this” identifier in a function.
In any case, here is my curry function:
var exfer = window.exfer || {};
exfer.curry = function (scope, func) {
var s, f, n;
// make the scope parameter optional such that it keeps the original this value
if (typeof scope == 'function' && typeof func != 'function') {
s = null; f = scope; n = 1;
} else {
s = scope; f = func; n = 2;
}
// save the arguments to pass to the new function
var args = Array.prototype.slice.call(arguments, n);
return (function () {
// override the this scope if desired
var o = s || this;
// add the arguments passed in to the other arguments
var allArgs = args.concat(Array.prototype.slice.call(arguments, 0));
// call the function with the new scope and arguments
return (f.apply(o, allArgs));
});
};
It differs from Dustin’s in two ways. First of all it can return a value which can be useful for some callbacks even if it isn’t so useful in the example I have above. Second, it can accept arguments from the calling function. Perhaps another example is in order.
Another Curry Demo
<a id="demo1" href="#">Curry demo</a>
<a id="demo2" href="#">Curry demo 2</a>
<a id="demo3" href="#">Curry demo 3</a>
<script type="text/javascript">
var arr = [ document.getElementById("demo1"),
document.getElementById("demo2"),
document.getElementById("demo3") ];
for (i in arr) {
arr[i].onclick = exfer.curry(arr[i], function (e) {
e = e || window.event;
this.innerHTML = this.innerHTML + "!";
if (e.preventDefault) {
e.preventDefault();
} else {
e.returnValue = false;
}
return (false);
});
}
</script>
Curry demo Curry demo 2 Curry demo 3
Here you can see an argument is passed to the curry function and it also returns a value. These are the two additional features added to Dustin’s curry function. They are pretty simple additions but very useful. I recommend playing around with a curry function if you use a lot of callbacks to see how you like it.



Post a Comment