28e65a85a625f7c0689bcf96ccf6043d

I use this little function everywhere in my javascript to deal with scope issues. Can anyone make it better?

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 !

Bfec5f7d1a4aaafc5a2451be8c42d26a

macournoyer

October 18, 2007, October 18, 2007 18:26, permalink

No rating. Login to rate!

hey richard could you provide an example usage of that function ?

A9ed3a0f3f9cd1d56b9436065bde3f14

kentaromiura

October 18, 2007, October 18, 2007 23:03, permalink

No rating. Login to rate!

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')}()
);
28e65a85a625f7c0689bcf96ccf6043d

richardhealy

October 19, 2007, October 19, 2007 08:45, permalink

No rating. Login to rate!

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>
Bfec5f7d1a4aaafc5a2451be8c42d26a

macournoyer

October 19, 2007, October 19, 2007 15:16, permalink

No rating. Login to rate!

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));
403e57e2be130d2218f992b86dfa8260

Gary Haran

October 30, 2007, October 30, 2007 15:41, permalink

1 rating. Login to rate!

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));
Bfec5f7d1a4aaafc5a2451be8c42d26a

macournoyer

October 30, 2007, October 30, 2007 15:48, permalink

No rating. Login to rate!

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

Your refactoring





Format Copy from initial code

or Cancel