Как определить имена аргументов функции в 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, и на самом деле это очень просто сделать:)