Как определить имена аргументов функции в javascript?

В AngularJS эти два объявления контроллера эквивалентны:

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

Оба $http и $scope будут правильными переменными независимо от того, в каком порядке они находятся. Т.е. переменная с именем $http всегда будет передана экземпляр службы $http.

Как Angular знает, какие объекты должны пройти и в каком порядке? Я думал, что такое отражение невозможно с помощью javascript.

Ответ 1

Если вы вызываете toString для функции, вы получаете js-объявление этой функции:

function a(b,c) {}

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

тогда вы можете проанализировать строку для порядка аргументов.

Некоторое исследование исходного кода angular подтверждает это:

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

Они строят функцию, затем извлекают аргументы с регулярным выражением и сохраняют их в массиве.

jsFiddle, показывающий, как все это работает.

Ответ 2

Хотя я не знаю, как они это делают, есть простой способ сделать это.

Все в JS имеет метод toString(). Для функций он показывает исходный код этой конкретной функции (для встроенных функций вы можете получить что-то вроде function() { [native code] }).

Найдем первый ( и первый ), которые включают аргументы функции. Затем разделите пробелы и разделите аргументы на ,. Voila, мы получаем массив имен аргументов.

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

Итак, давайте протестируем его:

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

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

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

Скрипка: http://jsfiddle.net/jYPB8/

Однако обратите внимание, что это поведение toString() определено в Function.prototype и может быть переопределено - в этом случае этот алгоритм не будет работать.

Итак, хотя это не может быть фактическое решение, которое вы искали, я хотел показать вам, что такое отражение возможно в JavaScript, и на самом деле это очень просто сделать:)