1 2 3 4 5 6 7 8 9 10
function curry(fn, scope) { var scope = scope || window; var args = Array.prototype.slice.call(arguments, 2) || []; return function() { fn.apply(scope, args); }; };
Refactorings
No refactoring yet !
macournoyer
October 18, 2007, October 18, 2007 18:26, permalink
hey richard could you provide an example usage of that function ?
kentaromiura
October 18, 2007, October 18, 2007 23:03, permalink
why these 2 are different?
1
1 2 3 4 5 6
alert( curry( function(){return this}, 'a' )() );
2
1 2 3
alert( function(){function(){return this;}.apply('a')}() );
richardhealy
October 19, 2007, October 19, 2007 08:45, permalink
Let me show you how I am using it. It might help to show you how I am implementing this. This is my rating widget that I use my Currying function in (lines 43 and 44). You can see the example working here: http://code.richardhealy.co.uk/yui/rating/index.htm
Javascript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
function curry(fn, scope) { var scope = scope || window; var args = Array.prototype.slice.call(arguments, 2) || []; return function() { fn.apply(scope, args); }; }; var rating = function(id, value) { this.construct.apply(this, arguments); }; rating.prototype = { construct: function(id, stars, value) { this.__timeout = -1; this.__listeners = {}; this.__timeouts = {}; this.id = id; this.imageOff = 'assets/blank.gif'; this.imageOn = 'assets/gSelected.gif'; this.imageOut = 'assets/bSelected.gif'; this.timeout = 500; this.value = value; this.stars = stars; var outsideEl = document.getElementById(id); for(i=1; i<=this.stars; i++){ var imgID = 'img'+i; var newimg = document.createElement('img'); newimg.id = imgID; newimg.src = this.imageOff; //Appends Image within the external container outsideEl.appendChild(newimg); YAHOO.util.Event.addListener(document.getElementById(imgID), "mouseover", curry(this.mouseOver, this, i)); //<--- Currying Used Here YAHOO.util.Event.addListener(document.getElementById(imgID), "click", curry(this.clickMethod, this, i)); //<--- Currying Used Here } this.addMethodListener("mouseOut", this.id, "mouseout"); this.renderStars(this.value, false); }, addMethodListener: function(method, el, event) { var that = this; this.__listeners["method:" + name + ":" + el.id + ":" + event] = function() { that[method].apply(that, arguments); }; YAHOO.util.Event.addListener(el, event, this.__listeners["method:" + name + ":" + el.id + ":" + event]); }, mouseOver: function(rating) { this.clearTimeout(this.__timeout); this.__timeout = -1; this.renderStars(rating, true); }, clickMethod: function(rating) { this.value = rating; this.onClick(rating); }, mouseOut: function() { if(this.__timeout != -1) { this.clearTimeout(this.__timeout); } this.__timeout = this.setTimeout('onTimeOut',this.timeout); }, onTimeOut: function() { this.renderStars(this.value, false); }, renderStars: function(units, startColor) { for (var i = 1; i <= units; i++) { if(startColor == true) { document.getElementById("img" + i).src = this.imageOn; } else { document.getElementById("img" + i).src = this.imageOut; } } for (i = units + 1; i <= this.stars; i++) { document.getElementById("img" + i).src = this.imageOff; } }, setTimeout: function(method, period) { this.clearTimeout(method); var that = this; var args = Array.prototype.slice.call(arguments, 2) || []; this.__timeouts[method] = setTimeout(function() { that[method].apply(that, args); }, period); }, clearTimeout: function(method) { if (this.__timeouts[method] > 0) { clearTimeout(this.__timeouts[method]); this.__timeouts[method] = 0; } }, onClick: function(value) { this.value = value; } }
HTML
1 2 3 4 5 6 7
<div id="rater" class="bk-form-rating rater"> <div class="bk-form-inner"></div> </div> <div id="actions"><button id="getVal" onclick="alert(r.value);">Alert Value</button></div> <script> var r = new rating('rater', 5, 0); </script>
macournoyer
October 19, 2007, October 19, 2007 15:16, permalink
why renderStars onMouseOver rather the onClick ? The delay feels like something is wrong.
But I think I understand w/ currying is good for in js. Thx Richard!
I don't know YUI lib but w/ Prototype I would write:
1 2 3 4 5 6 7
YAHOO.util.Event.addListener(document.getElementById(imgID), "mouseover", curry(this.mouseOver, this, i)); # W/ Prototype $(imgID).observe("mouseover", curry(this.mouseOver, this, i)); # W/out currying $(imgID).observe("mouseover", function() { this.mouseOver(i) }.bind(this));
Gary Haran
October 30, 2007, October 30, 2007 15:41, permalink
I would use bindAsEventListener here so that you can use the event.
1 2 3 4 5
# W/ Prototype and W/out currying $(imgID).observe("mouseover", function(event) { this.mouseOver(i) }.bindAsEventListener(this));
macournoyer
October 30, 2007, October 30, 2007 15:48, permalink
Prototype 1.6 is gonna support currying : http://www.prototypejs.org/2007/8/15/prototype-1-6-0-release-candidate
Here's the example the've put in the RC announcement
1 2 3 4 5 6 7
function sum(a, b) { return a + b; } sum(10, 5) // 15 var addTen = sum.curry(10); addTen(5) // 15
I use this little function everywhere in my javascript to deal with scope issues. Can anyone make it better?