28e65a85a625f7c0689bcf96ccf6043d

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

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?

alert(
	curry(
		function(){return this},
		'a'
	)()
);
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

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;
	}
}
<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:

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.

# 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

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