How to identify names of arguments to a function in javascript?

In AngularJS these two controller declarations are equivalent:

function BlahCtrl($scope, $http) { ... }
function BlahCtrl($http, $scope) { ... }

Both $http and $scope will be the correct variables no matter what order they are in. i.e. the variable named $http will always be passed an instance of the $http service.

How does Angular know which objects to pass in and in what order? I thought this kind of reflection was not possible with javascript.

If you call toString on a function, you get the js declaration of that function:

function a(b,c) {}

a.toString();  // "function a(b,c){}"

then you can parse the string for the order of arguments.

Some investigation into the angular source code confirms this:

if (typeof fn == 'function') {
  if (!($inject = fn.$inject)) {
    $inject = [];
    fnText = fn.toString().replace(STRIP_COMMENTS, '');
    argDecl = fnText.match(FN_ARGS);
    forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){
      arg.replace(FN_ARG, function(all, underscore, name){
        $inject.push(name);
      });
    });
    fn.$inject = $inject;
  }
}

They stringify the function, then extract the arguments with a regular expression and store them in an array.

jsFiddle showing how this all works.

Although I don't know how they do it in, there is a simple way to do it.

Everything in JS has toString() method. For functions, it shows the source code of that particular function (for in-built functions, you may get something like function() { [native code] }).

Let us find the first ( and the first ), which enclose the function's arguments. Then, let's strip the whitespaces and split the arguments by ,. Voila, we get an array of argument names.

function a($scope, $http) { };
function b($http, $scope) { };

function getParameterList(f) {
  var s = f.toString();

  var start = s.indexOf('(');
  var end = s.indexOf(')');

  s = s.substring(start + 1, end);

  return s.replace(/ /g,'').split(',');
}

So let's test it:

var aParams = getParameterList(a);
var bParams = getParameterList(b); 

alert(aParams[0]); // $scope
alert(aParams[1]); // $http 

alert(bParams[0]); // $http
alert(bParams[1]); // $scope  

A fiddle: http://jsfiddle.net/jYPB8/

However, note that this behaviour of toString() is defined in Function.prototype and may be redefined - in which case, this algorithm won't work.

So while this may not be the actual solution you were looking for, I wanted to show you that this kind of reflection is possible in JavaScript, and it's actually very simple to do :)