Reputation: 15800
This is an answer to a SO question:
function typedFunction(paramsList, f){
//optionally, ensure that typedFunction is being called properly -- here's a start:
if (!(paramsList instanceof Array)) throw Error('invalid argument: paramsList must be an array');
//the type-checked function
return function(){
for(var i=0,p,arg;p=paramsList[i],arg=arguments[i],i<paramsList.length; i++){
if (typeof p === 'string'){
if (typeof arg !== p) throw new Error('expected type ' + p + ', got ' + typeof arg);
}
else { //function
if (!(arg instanceof p)) throw new Error('expected type ' + String(p).replace(/\s*\{.*/, '') + ', got ' + typeof arg);
}
}
//type checking passed; call the function itself
return f.apply(this, arguments);
};
}
//usage:
var ds = typedFunction([Date, 'string'], function(d, s){
console.log(d.toDateString(), s.substr(0));
});
ds('notadate', 'test');
//Error: expected type function Date(), got string
ds();
//Error: expected type function Date(), got undefined
ds(new Date(), 42);
//Error: expected type string, got number
ds(new Date(), 'success');
//Fri Jun 14 2013 success
It shows a generalized way of ensuring arguments conform to specified types. I am having a hard time grasping the flow of arguments from the ds
function call, back to the function that is returned from typedFunction()
.
Here's what I expect:
paramsList
should refer to the first parameter in typedFunction()
's functionarguments
should refer to a list of all arguments passed to typedFunction
, in this case both paramsList
and f
. However, it seems like arguments
refers to the arguments passed in ds
. That's where things get fuzzy. How do the arguments supplied in ds
get passed to f
? Sorry if the question is not clear. Please edit the question if you have a grasp of what I'm trying to ask and can make the question easier to read.
Upvotes: 2
Views: 92
Reputation: 35491
For the sake of explanation, assume we name the function being returned as typeChecker
:
function typedFunction(paramsList, f){
// ...
return function typeChecker() { ... }
}
Now, here's the rundown of what is happening:
Running
var ds = typedFunction([Date, 'string'], function(d, s){
console.log(d.toDateString(), s.substr(0));
});
binds [Date, 'string']
as paramsList
and function(d, s) { ... }
as f
in the outer environment (closure) of typeChecker
. This means typeChecker
will, when called, have access to this data via the specified bound variable names paramsList
and f
.
Then we run:
ds(new Date(), 'success');
which essentially runs typeChecker(new Date(), 'success')
.
Side note: within any normal JS function (arrow function behaves differently), arguments is a special array-like object provided when the function is called. This object contains all the arguments passed into the function, irrelevant of how many parameters are specified in the function signature.
This means that, when we invoke ds(new Date(), 'success')
, arguments
conceptually looks like this:
[dateInstance, 'success']
although technically, it looks more like this:
{
0: dateInstance,
1: 'success'
}
but this makes no difference in the given code since both structures provide numeric property/index access being used in the for
loop.
Therefore, inside typeChecker
, arg
will refer to the item at the corresponding index in the arguments
object for each loop iteration, i.e. arg=arguments[i]
.
Once the iteration is finished, this runs:
return f.apply(this, arguments);
which calls the function initially given to typedFunction
as its second parameter, the one that was bound to the name f
in typeChecker
's closure, supplying this as the this
from within typeChecker
and passing along arguments from typeChecker
.
Upvotes: 2