I’ve recently started learning Javascript. As a C++ programmer, I thought I’d write a Javascript version of Boost.Bind; just for fun. Javascript closures make this a fairly simple task, actually.
Let’s get right into the code.
function bind(f)
{
if (f === null)
return function() {};
var args = Array.prototype.slice.call(arguments);
args.shift();
return function ()
{
var argsCopy = args.slice(0);
var start = expandArgs(argsCopy, arguments);
return f.apply(null, argsCopy.concat(Array.prototype.slice.call(arguments, start)));
};
}
Simple enough? The expandArgs function will be shown at the end. Now for some example usage.
function foo(a, b, c)
{
alert(a + ', ' + b + ', ' + c);
}
bind(foo, 1)(2, 3); // 1, 2, 3 bind(foo, 1, 2)(3); // 1, 2, 3 bind(foo, _3, _2, _1)(1, 2, 3); // 3, 2, 1 bind(foo, _2, _2, _2)(1, 2, 3); // 2, 2, 2 bind(foo, _2, 10)(1, 2, 3); // 2, 10, 3
Another function, called bindMember, will bind a function to an object, so the this object refers to it. It also does the same parameter binding as the original bind.
function bindMember(f, o)
{
if (f === null)
return function() {};
else if (o === null)
return f;
var args = Array.prototype.slice.call(arguments);
args.shift();
args.shift();
return function ()
{
var start = 0;
if (o.constructor == BindArg)
{
var arg = o.index;
o = arguments[arg - 1];
if (arg > start)
start = arg;
}
var argsCopy = args.slice(0);
start = expandArgs(argsCopy, arguments, start);
return f.apply(o, argsCopy.concat(Array.prototype.slice.call(arguments, start)));
};
}
As you can see, it’s very similar to bind, except it does an extra check to see if the object passed was a bind-argument. Here are some examples of its usage.
var o = { name : 'someobject' };
function foo(a, b, c)
{
alert(this.name + ', ' + a + ', ' + b + ', ' + c);
}
bindMember(foo, o, 1)(2, 3); // someobject, 1, 2, 3 bindMember(foo, o, 1, 2)(3); // someobject, 1, 2, 3 bindMember(foo, o, _3, _2, _1)(1, 2, 3); // someobject, 3, 2, 1 bindMember(foo, _1, 10, _2, _3)(o, 1, 2); // someobject, 10, 1, 2
That’s it. Be aware that this was merely done as an experiment, with the purpose of learning Javascript. I have not done any benchmarks to study the efficiency of these functions. If anyone has time to do them, please let me know the results. 🙂
Note that you could also make these functions part of the Function prototype, if you wish. That would allow writing foo.bind(1)(2, 3) if you find that more readable.
The rest of the code is as follows:
function BindArg(i)
{
this.index = i;
}
for (var i=1; i < 10; ++i)
{
var arg = new BindArg(i);
this['_' + i] = arg;
}
function expandArgs(args, arguments, start)
{
if (start == null)
start = 0;
for (var i=0; i < args.length; ++i)
{
if (args[i] && args[i].constructor == BindArg)
{
var arg = args[i].index;
if (arg > 0 && arg <= arguments.length)
{
args[i] = arguments[arg - 1];
if (arg > start)
start = arg;
}
else args[i] = null;
}
}
return start;
}