Вопрос по javascript – Как получить имена / значения параметров функции динамически?

272

Есть ли способ получить имена параметров функции динамически?

Допустим, моя функция выглядит следующим образом:

<code>function doSomething(param1, param2, .... paramN){
   // fill an array with the parameter name and value
   // some other code 
}
</code>

Теперь, как мне получить список имен параметров и их значений в массиве внутри функции?

Что такое CW? (Серьезно, я хотел бы знать эти сокращения, если я никогда не слышал их.) peter_the_oak
Не обязательно быть CW. Ólafur Waage
@peter_the_oak Сообщество вики. norcalli
Как мне удалить это из CW? vikasde
давайте идти вперед и пометить это как ответили приятель. новые ответы не приходят. Matthew Graves

Ваш Ответ

30   ответов
2

как получить список параметров, но вы можете сделать это, чтобы узнать, сколько он ожидает.

alert(doSomething.length);
function gotcha (a, b = false, c) {}; alert(gotcha.length)
1

что это похоронят. Тем не менее, я подумал, что это может быть полезно для некоторых.

Я не был полностью удовлетворен выбранными ответами, поскольку в ES6 он не очень хорошо работает со значениями по умолчанию. И это также не предоставляет информацию о значении по умолчанию. Я также хотел облегченную функцию, которая не зависит от внешней библиотеки.

Эта функция очень полезна для целей отладки, например: регистрация вызываемой функции с ее параметрами, значениями параметров по умолчанию и аргументами.

Я потратил некоторое время на это вчера, взламывая правильный RegExp для решения этой проблемы, и это то, что я придумал. Это работает очень хорошо, и я очень доволен результатом:

const REGEX_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
const REGEX_FUNCTION_PARAMS = /(?:\s*(?:function\s*[^(]*)?\s*)((?:[^'"]|(?:(?:(['"])(?:(?:.*?[^\\]\2)|\2))))*?)\s*(?=(?:=>)|{)/m
const REGEX_PARAMETERS_VALUES = /\s*(\w+)\s*(?:=\s*((?:(?:(['"])(?:\3|(?:.*?[^\\]\3)))((\s*\+\s*)(?:(?:(['"])(?:\6|(?:.*?[^\\]\6)))|(?:[\w$]*)))*)|.*?))?\s*(?:,|$)/gm

/**
 * Retrieve a function's parameter names and default values
 * Notes:
 *  - parameters with default values will not show up in transpiler code (Babel) because the parameter is removed from the function.
 *  - does NOT support inline arrow functions as default values
 *      to clarify: ( name = "string", add = defaultAddFunction )   - is ok
 *                  ( name = "string", add = ( a )=> a + 1 )        - is NOT ok
 *  - does NOT support default string value that are appended with a non-standard ( word characters or $ ) variable name
 *      to clarify: ( name = "string" + b )         - is ok
 *                  ( name = "string" + $b )        - is ok
 *                  ( name = "string" + b + "!" )   - is ok
 *                  ( name = "string" + λ )         - is NOT ok
 * @param {function} func
 * @returns {Array} - An array of the given function's parameter [key, default value] pairs.
 */
function getParams(func) {

  let functionAsString = func.toString()
  let params = []
  let match
  functionAsString = functionAsString.replace(REGEX_COMMENTS, '')
  functionAsString = functionAsString.match(REGEX_FUNCTION_PARAMS)[1]
  if (functionAsString.charAt(0) === '(') functionAsString = functionAsString.slice(1, -1)
  while (match = REGEX_PARAMETERS_VALUES.exec(functionAsString)) params.push([match[1], match[2]])
  return params

}



// Lets run some tests!

var defaultName = 'some name'

function test1(param1, param2, param3) { return (param1) => param1 + param2 + param3 }
function test2(param1, param2 = 4 * (5 / 3), param3) {}
function test3(param1, param2 = "/root/" + defaultName + ".jpeg", param3) {}
function test4(param1, param2 = (a) => a + 1) {}

console.log(getParams(test1)) 
console.log(getParams(test2))
console.log(getParams(test3))
console.log(getParams(test4))

// [ [ 'param1', undefined ], [ 'param2', undefined ], [ 'param3', undefined ] ]
// [ [ 'param1', undefined ], [ 'param2', '4 * (5 / 3)' ], [ 'param3', undefined ] ]
// [ [ 'param1', undefined ], [ 'param2', '"/root/" + defaultName + ".jpeg"' ], [ 'param3', undefined ] ]
// [ [ 'param1', undefined ], [ 'param2', '( a' ] ]
// --> This last one fails because of the inlined arrow function!


var arrowTest1 = (a = 1) => a + 4
var arrowTest2 = a => b => a + b
var arrowTest3 = (param1 = "/" + defaultName) => { return param1 + '...' }
var arrowTest4 = (param1 = "/" + defaultName, param2 = 4, param3 = null) => { () => param3 ? param3 : param2 }

console.log(getParams(arrowTest1))
console.log(getParams(arrowTest2))
console.log(getParams(arrowTest3))
console.log(getParams(arrowTest4))

// [ [ 'a', '1' ] ]
// [ [ 'a', undefined ] ]
// [ [ 'param1', '"/" + defaultName' ] ]
// [ [ 'param1', '"/" + defaultName' ], [ 'param2', '4' ], [ 'param3', 'null' ] ]


console.log(getParams((param1) => param1 + 1))
console.log(getParams((param1 = 'default') => { return param1 + '.jpeg' }))

// [ [ 'param1', undefined ] ]
// [ [ 'param1', '\'default\'' ] ]

Как вы можете сказать, некоторые имена параметров исчезают, потому что транспортера Babel удаляет их из функции. Если вы запустите это в последнем NodeJS, он будет работать как положено (прокомментированные результаты получены от NodeJS).

Другое замечание, как указано в комментарии, заключается в том, что он не работает с встроенными функциями стрелок в качестве значения по умолчанию. Это просто усложняет извлечение значений с помощью RegExp.

Пожалуйста, дайте мне знать, если это было полезно для вас! Хотелось бы услышать некоторые отзывы!

8

я считаю, что его самоанализ должен поддерживать получение имен параметров функций. Использование этой функциональности является нарушением первых принципов, поэтому я решил изучить эту проблему подробнее.

Это привело меня кэтот вопрос но нет встроенных решений. Что привело меня кэтот ответ что объясняет этоarguments только устарелаoutside функция, поэтому мы больше не можем использоватьmyFunction.arguments или мы получаем:

TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them

Время закатать рукава и приступить к работе:

& # X2B50; Для получения параметров функции требуется синтаксический анализатор, потому что сложные выражения, такие как4*(5/3) может использоваться в качестве значений по умолчанию. ТакГаафарский ответ или жеОтвет Джеймса Дрю Пока лучшие подходы.

Я попробовалВавилон а такжеesprima анализаторы, но, к сожалению, они не могут анализировать автономные анонимные функции, как указано вОтвет Матеуша Чаритонюка, Я нашел другой обходной путь, заключив код в круглые скобки, чтобы не менять логику:

const ast = parser.parse("(\n" + func.toString() + "\n)")

Новые строки предотвращают проблемы с// (однострочные комментарии).

& # X2B50; Если синтаксический анализатор недоступен, следующим лучшим вариантом будет использование проверенной методики, такой как регулярные выражения инжектора зависимостей Angular.js. Я объединил функциональную версиюОтвет Агнца сответ Хамблетима и добавил необязательныйARROW логический для контроля, разрешены ли функции жирной стрелки ES6 регулярными выражениями.

Вот два решения, которые я собрал. Обратите внимание, что в них нет логики для определения правильности синтаксиса функции, они только извлекают аргументы. Обычно это нормально, так как мы обычно передаем проанализированные функцииgetArguments() поэтому их синтаксис уже действителен.

Я постараюсь курировать эти решения как можно лучше, но без усилий со стороны сопровождающих JavaScript это останется открытой проблемой.

Node.js version (not runnable until StackOverflow supports Node.js):

const parserName = 'babylon';
// const parserName = 'esprima';
const parser = require(parserName);

function getArguments(func) {
    const maybe = function (x) {
        return x || {}; // optionals support
    }

    try {
        const ast = parser.parse("(\n" + func.toString() + "\n)");
        const program = parserName == 'babylon' ? ast.program : ast;

        return program
            .body[0]
            .expression
            .params
            .map(function(node) {
                return node.name || maybe(node.left).name || '...' + maybe(node.argument).name;
            });
    } catch (e) {
        return []; // could also return null
    }
};

////////// TESTS //////////

function logArgs(func) {
	let object = {};

	object[func] = getArguments(func);

	console.log(object);
// 	console.log(/*JSON.stringify(*/getArguments(func)/*)*/);
}

console.log('');
console.log('////////// MISC //////////');

logArgs((a, b) => {});
logArgs((a, b = 1) => {});
logArgs((a, b, ...args) => {});
logArgs(function(a, b, ...args) {});
logArgs(function(a, b = 1, c = 4 * (5 / 3), d = 2) {});
logArgs(async function(a, b, ...args) {});
logArgs(function async(a, b, ...args) {});

console.log('');
console.log('////////// FUNCTIONS //////////');

logArgs(function(a, b, c) {});
logArgs(function() {});
logArgs(function named(a, b, c) {});
logArgs(function(a /* = 1 */, b /* = true */) {});
logArgs(function fprintf(handle, fmt /*, ...*/) {});
logArgs(function(a, b = 1, c) {});
logArgs(function(a = 4 * (5 / 3), b) {});
// logArgs(function (a, // single-line comment xjunk) {});
// logArgs(function (a /* fooled you {});
// logArgs(function (a /* function() yes */, \n /* no, */b)/* omg! */ {});
// logArgs(function ( A, b \n,c ,d \n ) \n {});
logArgs(function(a, b) {});
logArgs(function $args(func) {});
logArgs(null);
logArgs(function Object() {});

console.log('');
console.log('////////// STRINGS //////////');

logArgs('function (a,b,c) {}');
logArgs('function () {}');
logArgs('function named(a, b, c) {}');
logArgs('function (a /* = 1 */, b /* = true */) {}');
logArgs('function fprintf(handle, fmt /*, ...*/) {}');
logArgs('function( a, b = 1, c ) {}');
logArgs('function (a=4*(5/3), b) {}');
logArgs('function (a, // single-line comment xjunk) {}');
logArgs('function (a /* fooled you {}');
logArgs('function (a /* function() yes */, \n /* no, */b)/* omg! */ {}');
logArgs('function ( A, b \n,c ,d \n ) \n {}');
logArgs('function (a,b) {}');
logArgs('function $args(func) {}');
logArgs('null');
logArgs('function Object() {}');

Полный рабочий пример:

https://repl.it/repls/SandybrownPhonyAngles

Browser version (note that it stops at the first complex default value):

function getArguments(func) {
    const ARROW = true;
    const FUNC_ARGS = ARROW ? /^(function)?\s*[^\(]*\(\s*([^\)]*)\)/m : /^(function)\s*[^\(]*\(\s*([^\)]*)\)/m;
    const FUNC_ARG_SPLIT = /,/;
    const FUNC_ARG = /^\s*(_?)(.+?)\1\s*$/;
    const STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;

    return ((func || '').toString().replace(STRIP_COMMENTS, '').match(FUNC_ARGS) || ['', '', ''])[2]
        .split(FUNC_ARG_SPLIT)
        .map(function(arg) {
            return arg.replace(FUNC_ARG, function(all, underscore, name) {
                return name.split('=')[0].trim();
            });
        })
        .filter(String);
}

////////// TESTS //////////

function logArgs(func) {
	let object = {};

	object[func] = getArguments(func);

	console.log(object);
// 	console.log(/*JSON.stringify(*/getArguments(func)/*)*/);
}

console.log('');
console.log('////////// MISC //////////');

logArgs((a, b) => {});
logArgs((a, b = 1) => {});
logArgs((a, b, ...args) => {});
logArgs(function(a, b, ...args) {});
logArgs(function(a, b = 1, c = 4 * (5 / 3), d = 2) {});
logArgs(async function(a, b, ...args) {});
logArgs(function async(a, b, ...args) {});

console.log('');
console.log('////////// FUNCTIONS //////////');

logArgs(function(a, b, c) {});
logArgs(function() {});
logArgs(function named(a, b, c) {});
logArgs(function(a /* = 1 */, b /* = true */) {});
logArgs(function fprintf(handle, fmt /*, ...*/) {});
logArgs(function(a, b = 1, c) {});
logArgs(function(a = 4 * (5 / 3), b) {});
// logArgs(function (a, // single-line comment xjunk) {});
// logArgs(function (a /* fooled you {});
// logArgs(function (a /* function() yes */, \n /* no, */b)/* omg! */ {});
// logArgs(function ( A, b \n,c ,d \n ) \n {});
logArgs(function(a, b) {});
logArgs(function $args(func) {});
logArgs(null);
logArgs(function Object() {});

console.log('');
console.log('////////// STRINGS //////////');

logArgs('function (a,b,c) {}');
logArgs('function () {}');
logArgs('function named(a, b, c) {}');
logArgs('function (a /* = 1 */, b /* = true */) {}');
logArgs('function fprintf(handle, fmt /*, ...*/) {}');
logArgs('function( a, b = 1, c ) {}');
logArgs('function (a=4*(5/3), b) {}');
logArgs('function (a, // single-line comment xjunk) {}');
logArgs('function (a /* fooled you {}');
logArgs('function (a /* function() yes */, \n /* no, */b)/* omg! */ {}');
logArgs('function ( A, b \n,c ,d \n ) \n {}');
logArgs('function (a,b) {}');
logArgs('function $args(func) {}');
logArgs('null');
logArgs('function Object() {}');

Полный рабочий пример:

https://repl.it/repls/StupendousShowyOffices

если я не ошибаюсь, в вашей версии браузера первое условие в FUNC_ARGS будет работать как для стрелки, так и для традиционных функций, так что вам не нужна вторая часть, и поэтому вы можете покончить с зависимостью от ARROW.
Это замечательно! Я искал решение, подобное этому, которое использует синтаксический анализатор для покрытия синтаксиса ES6. Я планирую использовать это для создания jest & # x201C; реализует интерфейс & # x201D; совпадение, потому что простое использование function.length имеет ограничения с параметрами по умолчанию, и я хотел иметь возможность утверждать остальные параметры.
Стоит отметить, что пятый контрольный пример, содержащий скобки в значении по умолчанию, в настоящее время не выполняется. Я хотел бы, чтобы мое регулярное выражение было достаточно сильным, чтобы исправить, извините!
7
(function(a,b,c){}).toString().replace(/.*\(|\).*/ig,"").split(',')

,
0

Вот мое предложение, которое не использует регулярные выражения, за исключением простой задачи по удалению пробелов. (Следует отметить, что функция «strips_comments» фактически удаляет их, а не удаляет их физически. Это потому, что я использую его в другом месте и по разным причинам нуждаюсь в расположении оригинальных токенов без комментариев, чтобы они не затрагивались)

Это довольно длинный блок кода, так как эта вставка включает в себя мини-тестовую среду.

    function do_tests(func) {

    if (typeof func !== 'function') return true;
    switch (typeof func.tests) {
        case 'undefined' : return true;
        case 'object'    : 
            for (var k in func.tests) {

                var test = func.tests[k];
                if (typeof test==='function') {
                    var result = test(func);
                    if (result===false) {
                        console.log(test.name,'for',func.name,'failed');
                        return false;
                    }
                }

            }
            return true;
        case 'function'  : 
            return func.tests(func);
    }
    return true;
} 
function strip_comments(src) {

    var spaces=(s)=>{
        switch (s) {
            case 0 : return '';
            case 1 : return ' ';
            case 2 : return '  ';
        default : 
            return Array(s+1).join(' ');
        }
    };

    var c1 = src.indexOf ('/*'),
        c2 = src.indexOf ('//'),
        eol;

    var out = "";

    var killc2 = () => {
                out += src.substr(0,c2);
                eol =  src.indexOf('\n',c2);
                if (eol>=0) {
                    src = spaces(eol-c2)+'\n'+src.substr(eol+1);
                } else {
                    src = spaces(src.length-c2);
                    return true;
                }

             return false;
         };

    while ((c1>=0) || (c2>=0)) {
         if (c1>=0) {
             // c1 is a hit
             if ( (c1<c2) || (c2<0) )  {
                 // and it beats c2
                 out += src.substr(0,c1);
                 eol = src.indexOf('*/',c1+2);
                 if (eol>=0) {
                      src = spaces((eol-c1)+2)+src.substr(eol+2);
                 } else {
                      src = spaces(src.length-c1);
                      break;
                 }
             } else {

                 if (c2 >=0) {
                     // c2 is a hit and it beats c1
                     if (killc2()) break;
                 }
             }
         } else {
             if (c2>=0) {
                // c2 is a hit, c1 is a miss.
                if (killc2()) break;  
             } else {
                 // both c1 & c2 are a miss
                 break;
             }
         }

         c1 = src.indexOf ('/*');
         c2 = src.indexOf ('//');   
        }

    return out + src;
}

function function_args(fn) {
    var src = strip_comments(fn.toString());
    var names=src.split(')')[0].replace(/\s/g,'').split('(')[1].split(',');
    return names;
}

function_args.tests = [

     function test1 () {

            function/*al programmers will sometimes*/strip_comments_tester/* because some comments are annoying*/(
            /*see this---(((*/ src//)) it's an annoying comment does not help anyone understand if the 
            ,code,//really does
            /**/sucks ,much /*?*/)/*who would put "comment\" about a function like (this) { comment } here?*/{

            }


        var data = function_args(strip_comments_tester);

        return ( (data.length==4) &&
                 (data[0]=='src') &&
                 (data[1]=='code') &&
                 (data[2]=='sucks') &&
                 (data[3]=='much')  );

    }

];
do_tests(function_args);
1
function getArgs(args) {
    var argsObj = {};

    var argList = /\(([^)]*)/.exec(args.callee)[1];
    var argCnt = 0;
    var tokens;

    while (tokens = /\s*([^,]+)/g.exec(argList)) {
        argsObj[tokens[1]] = args[argCnt++];
    }

    return argsObj;
}
15

это хорошо, но это не слишком хорошо обрабатывает новые дополнения к языку (такие как функции стрелок и классы). Также следует отметить, что если вы используете какую-либо из этих функций в минимизированном коде, это будет происходить. Он будет использовать любое минимизированное имя. Angular обходит это, позволяя вам передавать упорядоченный массив строк, который соответствует порядку аргументов при регистрации их в контейнере DI. Итак, с решением:

var esprima = require('esprima');
var _ = require('lodash');

const parseFunctionArguments = (func) => {
    // allows us to access properties that may or may not exist without throwing 
    // TypeError: Cannot set property 'x' of undefined
    const maybe = (x) => (x || {});

    // handle conversion to string and then to JSON AST
    const functionAsString = func.toString();
    const tree = esprima.parse(functionAsString);
    console.log(JSON.stringify(tree, null, 4))
    // We need to figure out where the main params are. Stupid arrow functions 
    const isArrowExpression = (maybe(_.first(tree.body)).type == 'ExpressionStatement');
    const params = isArrowExpression ? maybe(maybe(_.first(tree.body)).expression).params 
                                     : maybe(_.first(tree.body)).params;

    // extract out the param names from the JSON AST
    return _.map(params, 'name');
};

Это обрабатывает исходную проблему разбора и еще несколько типов функций (например, функции стрелок). Вот идея того, что он может и не может обрабатывать как есть:

// I usually use mocha as the test runner and chai as the assertion library
describe('Extracts argument names from function signature. ', () => {
    const test = (func) => {
        const expectation = ['it', 'parses', 'me'];
        const result = parseFunctionArguments(toBeParsed);
        result.should.equal(expectation);
    } 

    it('Parses a function declaration.', () => {
        function toBeParsed(it, parses, me){};
        test(toBeParsed);
    });

    it('Parses a functional expression.', () => {
        const toBeParsed = function(it, parses, me){};
        test(toBeParsed);
    });

    it('Parses an arrow function', () => {
        const toBeParsed = (it, parses, me) => {};
        test(toBeParsed);
    });

    // ================= cases not currently handled ========================

    // It blows up on this type of messing. TBH if you do this it deserves to 
    // fail  On a tech note the params are pulled down in the function similar 
    // to how destructuring is handled by the ast.
    it('Parses complex default params', () => {
        function toBeParsed(it=4*(5/3), parses, me) {}
        test(toBeParsed);
    });

    // This passes back ['_ref'] as the params of the function. The _ref is a 
    // pointer to an VariableDeclarator where the ✨ happens.
    it('Parses object destructuring param definitions.' () => {
        function toBeParsed ({it, parses, me}){}
        test(toBeParsed);
    });

    it('Parses object destructuring param definitions.' () => {
        function toBeParsed ([it, parses, me]){}
        test(toBeParsed);
    });

    // Classes while similar from an end result point of view to function
    // declarations are handled completely differently in the JS AST. 
    it('Parses a class constructor when passed through', () => {
        class ToBeParsed {
            constructor(it, parses, me) {}
        }
        test(ToBeParsed);
    });
});

В зависимости от того, что вы хотите использовать для прокси-серверов ES6 и деструктуризация, может быть лучшим выбором. Например, если вы хотите использовать его для внедрения зависимостей (используя имена параметров), вы можете сделать это следующим образом:

class GuiceJs {
    constructor() {
        this.modules = {}
    }
    resolve(name) {
        return this.getInjector()(this.modules[name]);
    }
    addModule(name, module) {
        this.modules[name] = module;
    }
    getInjector() {
        var container = this;

        return (klass) => {
            console.log(klass);
            var paramParser = new Proxy({}, {
                // The `get` handler is invoked whenever a get-call for
                // `injector.*` is made. We make a call to an external service
                // to actually hand back in the configured service. The proxy
                // allows us to bypass parsing the function params using
                // taditional regex or even the newer parser.
                get: (target, name) => container.resolve(name),

                // You shouldn't be able to set values on the injector.
                set: (target, name, value) => {
                    throw new Error(`Don't try to set ${name}! `);
                }
            })
            return new klass(paramParser);
        }
    }
}

Это не самый продвинутый распознаватель, но он дает представление о том, как вы можете использовать прокси-сервер для его обработки, если вы хотите использовать анализатор args для простого DI. Однако в этом подходе есть одна небольшая оговорка. Нам нужно использовать деструктурирующие назначения вместо обычных параметров. Когда мы передаем прокси инжектора, деструктурирование аналогично вызову геттера объекта.

class App {
   constructor({tweeter, timeline}) {
        this.tweeter = tweeter;
        this.timeline = timeline;
    }
}

class HttpClient {}

class TwitterApi {
    constructor({client}) {
        this.client = client;
    }
}

class Timeline {
    constructor({api}) {
        this.api = api;
    }
}

class Tweeter {
    constructor({api}) {
        this.api = api;
    }
}

// Ok so now for the business end of the injector!
const di = new GuiceJs();

di.addModule('client', HttpClient);
di.addModule('api', TwitterApi);
di.addModule('tweeter', Tweeter);
di.addModule('timeline', Timeline);
di.addModule('app', App);

var app = di.resolve('app');
console.log(JSON.stringify(app, null, 4));

Это выводит следующее:

{
    "tweeter": {
        "api": {
            "client": {}
        }
    },
    "timeline": {
        "api": {
            "client": {}
        }
    }
}

Его подключили все приложение. Лучше всего то, что приложение легко тестировать (вы можете просто создать экземпляр каждого класса и передать в mocks / stubs / etc). Также, если вам нужно поменять местами реализации, вы можете сделать это из одного места. Все это возможно благодаря объектам JS Proxy.

Note: There is a lot of work that would need to be done to this before it would be ready for production use but it does give an idea of what it would look like.

Это немного поздно в ответе, но это может помочь другим, кто думает о том же самом.

1

использованияжелудь.

const acorn = require('acorn');    

function f(a, b, c) {
   // ...
}

const argNames = acorn.parse(f).body[0].params.map(x => x.name);
console.log(argNames);  // Output: [ 'a', 'b', 'c' ]

Код здесь находит имена трех (формальных) параметров функцииf, Это делается путем кормленияf вacorn.parse().

0

оно не должно нарушать странные функции, чьиtoString() выглядит так же странно:

function  (  A,  b
,c      ,d
){}

screenshot from console

Кроме того, зачем использовать сложные регулярные выражения? Это можно сделать так:

function getArguments(f) {
    return f.toString().split(')',1)[0].replace(/\s/g,'').substr(9).split(',');
}

Это работает везде с каждой функцией, и единственным регулярным выражением является удаление пробелов, которое даже не обрабатывает всю строку из-за.split трюк.

0

переданных функции, используя & quot; arguments & quot; имущество.

    function doSomething()
    {
        var args = doSomething.arguments;
        var numArgs = args.length;
        for(var i = 0 ; i < numArgs ; i++)
        {
            console.log("arg " + (i+1) + " = " + args[i]);  
                    //console.log works with firefox + firebug
                    // you can use an alert to check in other browsers
        }
    }

    doSomething(1, '2', {A:2}, [1,2,3]);    
Викасде прав. Доступ кarguments свойство экземпляра функции устарело. Увидетьdeveloper.mozilla.org/en/Core_JavaScript_1.5_Reference/…
Разве аргументы не устарели? См. Предложение Ionut G. Stan выше. vikasde
299

ии.

var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
var ARGUMENT_NAMES = /([^\s,]+)/g;
function getParamNames(func) {
  var fnStr = func.toString().replace(STRIP_COMMENTS, '');
  var result = fnStr.slice(fnStr.indexOf('(')+1, fnStr.indexOf(')')).match(ARGUMENT_NAMES);
  if(result === null)
     result = [];
  return result;
}

Пример использования:

getParamNames(getParamNames) // returns ['func']
getParamNames(function (a,b,c,d){}) // returns ['a','b','c','d']
getParamNames(function (a,/*b,c,*/d){}) // returns ['a','d']
getParamNames(function (){}) // returns []

Edit:

С изобретением ES6 эта функция может быть отключена по умолчанию. Вот быстрый взлом, который должен работать в большинстве случаев:

var STRIP_COMMENTS = /(\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s*=[^,\)]*(('(?:\\'|[^'\r\n])*')|("(?:\\"|[^"\r\n])*"))|(\s*=[^,\)]*))/mg;

Я говорю, что большинство случаев, потому что есть некоторые вещи, которые запутают это

function (a=4*(5/3), b) {} // returns ['a']

Edit: I also note vikasde wants the parameter values in an array also. This is already provided in a local variable named arguments.

выдержка изhttps://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments:

Объект аргументов не является массивом. Он похож на массив, но не имеет никаких свойств массива, кроме длины. Например, у него нет метода pop. Однако он может быть преобразован в настоящий массив:

var args = Array.prototype.slice.call(arguments);

Если доступны универсальные массивы, вместо них можно использовать следующее:

var args = Array.slice(arguments);
Обратите внимание, это работает во всех браузерах
Обратите внимание, что это решение может дать сбой из-за комментариев и пробелов - например:var fn = function(a /* fooled you)*/,b){}; приведет к["a", "/*", "fooled", "you"]
Компиляция регулярного выражения обходится дорого, поэтому вы хотите избегать компиляции сложных регулярных выражений более одного раза. Вот почему это делается за пределами функции
Я изменил функцию, чтобы она возвращала пустой массив (вместо нуля), когда нет никаких аргументов
ИСПРАВЛЕНИЕ: собирался изменить регулярное выражение с помощью модификатора / s, который позволяет perl, так "." также может соответствовать новой строке. Это необходимо для многострочных комментариев внутри / * * /. Оказывается, регулярное выражение Javascript не разрешает модификатор / s. Исходное регулярное выражение с использованием [/ s / S] соответствует символам новой строки. СООО, пожалуйста, не обращайте внимания на предыдущий комментарий.
21

var fn = function(/* whoa) */ hi, you){};

fn.toString()
  .replace(/((\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s))/mg,'')
  .match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1]
  .split(/,/)

["hi", "you"]
@AlexMills Одна вещь, которую я заметил, заключается в том, что в Спецификации для функций со стрелками сказано, что они не должны рассматриваться как «функции». Это означает, что это не подходит для того, чтобы соответствовать функциям массива. «Это» не устанавливается таким же образом, и они также не должны вызываться как функции. Это было то, чему я научился трудным путем. ($ myService) = & gt; $ myService.doSomething () выглядит круто, но это неправильное использование функций массива.
не работает для функций жирной стрелки ES6 :)
0

// Utility function to extract arg name-value pairs
function getArgs(args) {
    var argsObj = {};

    var argList = /\(([^)]*)/.exec(args.callee)[1];
    var argCnt = 0;
    var tokens;
    var argRe = /\s*([^,]+)/g;

    while (tokens = argRe.exec(argList)) {
        argsObj[tokens[1]] = args[argCnt++];
    }

    return argsObj;
}

// Test subject
function add(number1, number2) {
    var args = getArgs(arguments);
    console.log(args); // ({ number1: 3, number2: 4 })
}

// Invoke test subject
add(3, 4);

Примечание. Это работает только в тех браузерах, которые поддерживаютarguments.callee.

С 2009 года все немного изменилось!
@ George2.0Hope Спасибо за указание на это. Я обновлю ответ.
Цикл while приводит к бесконечному циклу с предоставленным кодом (по состоянию на 20171121at1047EDT)
args.toSource не является функцией (строка 20) ... но даже если вы измените ее на: console.log (args.toString ()); ... вы получаете ... [объект объекта] ... лучше, если вы это сделаете: console.log (JSON.stringify (args));
в случае, если кто-то появится здесь для React, эта функция, которая является удивительной, не будет работать в строгом режиме.
11

I know this is an old question, but beginners have been copypasting this around as if this was good practice in any code. Most of the time, having to parse a function's string representation to use its parameter names just hides a flaw in the code's logic.

Параметры функции на самом деле хранятся в похожем на массив объектеargumentsгде первый аргументarguments[0]второеarguments[1] и так далее. Запись имен параметров в скобках можно рассматривать как сокращенный синтаксис. Это:

function doSomething(foo, bar) {
    console.log("does something");
}

...такой же как:

function doSomething() {
    var foo = arguments[0];
    var bar = arguments[1];

    console.log("does something");
}

Сами переменные хранятся в области действия функции, а не в качестве свойств объекта. Невозможно извлечь имя параметра через код, поскольку это просто символ, представляющий переменную на человеческом языке.

Я всегда рассматривал строковое представление функции как инструмент для целей отладки, особенно из-за этогоarguments массивоподобный объект. Вы не обязаны давать имена аргументам в первую очередь. Если вы попытаетесь разобрать строковую функцию, она на самом деле не скажет вам о дополнительных безымянных параметрах, которые она может принять.

Здесь ситуация еще хуже и более распространенная. Если функция имеет более 3 или 4 аргументов, может быть логично передать ей объект, с которым легче работать.

function saySomething(obj) {
  if(obj.message) console.log((obj.sender || "Anon") + ": " + obj.message);
}

saySomething({sender: "user123", message: "Hello world"});

В этом случае сама функция сможет прочитать полученный объект, найти его свойства и получить как их имена, так и значения, но попытка разобрать строковое представление функции даст вам только «obj». для параметров, которые вообще не полезны.

На "смешно" sidenote, вы можете заставить все функции работать так, как если бы они были анонимными встроенными программами, запустив это:Function.prototype.toString = function () { return 'function () { [native code] }'; };
Увидетьthis file for their parser, Это немного наивно, но обрабатывает большинство случаев.
Интересно, что я удивлен, что такая библиотека привлекла к себе внимание. Ну, есть много открытых вопросов о проблемных ситуациях, подобных тем, которые я описал. Как я уже сказал, если это для целей отладки, это нормально, но полагаться на преобразование функций в производственной среде слишком рискованно.
Я думаю, что причина для чего-то вроде этого обычно: отладка / ведение журнала, какой-то декоратор, который делает прикольные вещи (технический термин), или создание инфраструктуры внедрения зависимостей для ваших приложений для автоматического внедрения на основе имени аргумента (так работает angular ). Другой действительно интересный пример использования - это узел Promisify (это библиотека, которая принимает функцию, которая обычно принимает обратный вызов, а затем преобразует ее в Обещание). Они используют это, чтобы найти общие имена для обратных вызовов (например, cb / callback / и т. Д.), А затем они могут проверить, является ли функция асинхронной или синхронизированной, прежде чем обернуть ее.
Согласитесь, немного грязно с этим справляться. Лучшее и наиболее простое использование - внедрение зависимостей. В этом случае вы владеете кодом и можете обрабатывать наименования и другие области кода. Я думаю, что именно здесь это будет наиболее полезным. Я в настоящее время используюesprima (вместо регулярных выражений) и ES6Proxy (constructor trap а такжеapply trap) а такжеReflection обрабатывать DI для некоторых из моих модулей. Он достаточно твердый.
0

На первом есть устаревшийarguments.callee & # X2014; ссылка на вызываемую функцию. Во-вторых, если у вас есть ссылка на вашу функцию, вы можете легко получить их текстовое представление. В-третьих, если вы вызываете свою функцию в качестве конструктора, вы также можете иметь ссылку через yourObject.constructor. NB. Первое решение устарело, поэтому, если вы не можете его не использовать, вы также должны подумать об архитектуре своего приложения. Если вам не нужны точные имена переменных, просто используйте внутреннюю переменную функцииarguments без всякой магии.

https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/arguments/callee

Все они собираются вызвать toString и заменить на re, чтобы мы могли создать помощника:

// getting names of declared parameters
var getFunctionParams = function (func) {
    return String(func).replace(/[^\(]+\(([^\)]*)\).*/m, '$1');
}

Некоторые примеры:

// Solution 1. deprecated! don't use it!
var myPrivateFunction = function SomeFuncName (foo, bar, buz) {
    console.log(getFunctionParams(arguments.callee));
};
myPrivateFunction (1, 2);

// Solution 2.
var myFunction = function someFunc (foo, bar, buz) {
    // some code
};
var params = getFunctionParams(myFunction);
console.log(params);

// Solution 3.
var cls = function SuperKewlClass (foo, bar, buz) {
    // some code
};
var inst = new cls();
var params = getFunctionParams(inst.constructor);
console.log(params);

Наслаждайтесь с JS!

UPD: Джеку Аллану было предоставлено немного лучшее решение. GJ Джек!

Это может быть еще проще, если вы использовалиSomeFuncName вместоarguments.callee (оба указывают на сам объект функции).
5

esprima & quot; парсер, чтобы избежать многих проблем с комментариями, пробелами и другими вещами в списке параметров.

function getParameters(yourFunction) {
    var i,
        // safetyValve is necessary, because sole "function () {...}"
        // is not a valid syntax
        parsed = esprima.parse("safetyValve = " + yourFunction.toString()),
        params = parsed.body[0].expression.right.params,
        ret = [];

    for (i = 0; i < params.length; i += 1) {
        // Handle default params. Exe: function defaults(a = 0,b = 2,c = 3){}
        if (params[i].type == 'AssignmentPattern') {
            ret.push(params[i].left.name)
        } else {
            ret.push(params[i].name);
        }
    }

    return ret;
}

Он работает даже с таким кодом:

getParameters(function (hello /*, foo ),* /bar* { */,world) {}); // ["hello", "world"]
1

To get the values of the actual parameters passed to the function (let's call it argValues). This is straight forward as it will be available as arguments inside the function. To get the parameter names from the function signature (let's call it argNames). This not as easy and requires parsing the function. Instead of doing the complex regex yourself and worrying about edge cases (default parameters, comments, ...), you can use a library like babylon that will parse the function into an abstract syntax tree from which you can obtain the names of parameters. The last step is to join the 2 arrays together into 1 array that has the name and value of all the parameters.

Код будет таким

const babylon = require("babylon")
function doSomething(a, b, c) {
    // get the values of passed argumenst
    const argValues = arguments

    // get the names of the arguments by parsing the function
    const ast = babylon.parse(doSomething.toString())
    const argNames =  ast.program.body[0].params.map(node => node.name)

    // join the 2 arrays, by looping over the longest of 2 arrays
    const maxLen = Math.max(argNames.length, argValues.length)
    const args = []
    for (i = 0; i < maxLen; i++) { 
       args.push({name: argNames[i], value: argValues[i]})
    }
    console.log(args)

    // implement the actual function here
}

doSomething(1, 2, 3, 4)

и зарегистрированный объект будет

[
  {
    "name": "a",
    "value": 1
  },
  {
    "name": "c",
    "value": 3
  },
  {
    "value": 4
  }
]

И вот рабочий примерhttps://tonicdev.com/5763eb77a945f41300f62a79/5763eb77a945f41300f62a7a

33

которое пытается компактно решить все крайние случаи, упомянутые выше:

function $args(func) {  
    return (func + '')
      .replace(/[/][/].*$/mg,'') // strip single-line comments
      .replace(/\s+/g, '') // strip white space
      .replace(/[/][*][^/*]*[*][/]/g, '') // strip multi-line comments  
      .split('){', 1)[0].replace(/^[^(]*[(]/, '') // extract the parameters  
      .replace(/=[^,]+/g, '') // strip any ES6 defaults  
      .split(',&,apos;).filter(Boolean); // split & filter [""]
}  

Сокращенный результат теста (полные тестовые примеры прилагаются ниже):

'function (a,b,c)...' // returns ["a","b","c"]
'function ()...' // returns []
'function named(a, b, c) ...' // returns ["a","b","c"]
'function (a /* = 1 */, b /* = true */) ...' // returns ["a","b"]
'function fprintf(handle, fmt /*, ...*/) ...' // returns ["handle","fmt"]
'function( a, b = 1, c )...' // returns ["a","b","c"]
'function (a=4*(5/3), b) ...' // returns ["a","b"]
'function (a, // single-line comment xjunk) ...' // returns ["a","b"]
'function (a /* fooled you...' // returns ["a","b"]
'function (a /* function() yes */, \n /* no, */b)/* omg! */...' // returns ["a","b"]
'function ( A, b \n,c ,d \n ) \n ...' // returns ["A","b","c","d"]
'function (a,b)...' // returns ["a","b"]
'function $args(func) ...' // returns ["func"]
'null...' // returns ["null"]
'function Object() ...' // returns []

function $args(func) {  
    return (func + '')
      .replace(/[/][/].*$/mg,'') // strip single-line comments
      .replace(/\s+/g, '') // strip white space
      .replace(/[/][*][^/*]*[*][/]/g, '') // strip multi-line comments  
      .split('){', 1)[0].replace(/^[^(]*[(]/, '') // extract the parameters  
      .replace(/=[^,]+/g, '') // strip any ES6 defaults  
      .split(',').filter(Boolean); // split & filter [""]
}  

// test cases  
document.getElementById('console_info').innerHTML = (
[  
  // formatting -- typical  
  function(a,b,c){},  
  function(){},  
  function named(a, b,  c) {  
/* multiline body */  
  },  
    
  // default values -- conventional  
  function(a /* = 1 */, b /* = true */) { a = a||1; b=b||true; },  
  function fprintf(handle, fmt /*, ...*/) { },  
  
  // default values -- ES6  
  "function( a, b = 1, c ){}",  
  "function (a=4*(5/3), b) {}",  
  
  // embedded comments -- sardonic  
  function(a, // single-line comment xjunk) {}
    b //,c,d
  ) // single-line comment
  {},  
  function(a /* fooled you{*/,b){},  
  function /* are you kidding me? (){} */(a /* function() yes */,  
   /* no, */b)/* omg! */{/*}}*/},  
  
  // formatting -- sardonic  
  function  (  A,  b  
,c  ,d  
  )  
  {  
  },  
  
  // by reference  
  this.jQuery || function (a,b){return new e.fn.init(a,b,h)},
  $args,  
  
  // inadvertent non-function values  
  null,  
  Object  
].map(function(f) {
    var abbr = (f + '').replace(/\n/g, '\\n').replace(/\s+|[{]+$/g, ' ').split("{", 1)[0] + "...";
    return "    '" + abbr + "' // returns " + JSON.stringify($args(f));
  }).join("\n") + "\n"); // output for copy and paste as a markdown snippet
<pre id='console_info'></pre>

Это прерывается, когда присутствуют однострочные комментарии. Попробуй это:return (func+'') .replace(/[/][/].*$/mg,'') // strip single-line comments (line-ending sensitive, so goes first) .replace(/\s+/g,'') // remove whitespace
Хороший улов - я обновил пример вашим предложенным исправлением, а также добавил соответствующий тестовый пример во фрагмент кода. Спасибо!
Вы, вероятно, должны заменитьfunc + '' сFunction.toString.call(func) защищать от случая, когда функция имеет собственную реализацию .toString ().
толстые стрелки = & gt;.split(/\)[\{=]/, 1)[0]
Интересно, как защититься от этого, и хотел бы включить ваше предложение. Нефункциональные значения по-прежнему необходимо приводить - как вы думаете, стоит ли стремиться к компактности:(/^f/.test(typeof func) ? $args.toString.call(func) : func+'')или в этом случае лучше просто изложить это:(typeof func === 'function' ? Function.toString.call(func) : func+'')?
4

подходит ли это решение к вашей проблеме, но оно позволяет вам переопределить любую функцию, которую вы хотите, без необходимости изменять код, который ее использует. Существующие вызовы будут использовать позиционированные параметры, в то время как реализация функции может использовать «именованные параметры». (один хэш-параметр).

Я думал, что вы в любом случае измените существующие определения функций так, почему бы не иметь заводскую функцию, которая делает именно то, что вы хотите:

<!DOCTYPE html>

<html>
<head>
<meta charset="UTF-8">
<title></title>
<script type="text/javascript">
var withNamedParams = function(params, lambda) {
    return function() {
        var named = {};
        var max   = arguments.length;

        for (var i=0; i<max; i++) {
            named[params[i]] = arguments[i];
        }

        return lambda(named);
    };
};

var foo = withNamedParams(["a", "b", "c"], function(params) {
    for (var param in params) {
        alert(param + ": " + params[param]);
    }
});

foo(1, 2, 3);
</script>
</head>
<body>

</body>
</html>

Надеюсь, поможет.

1
//See this:


// global var, naming bB
var bB = 5;

//  Dependency Injection cokntroller
var a = function(str, fn) {
  //stringify function body
  var fnStr = fn.toString();

  // Key: get form args to string
  var args = fnStr.match(/function\s*\((.*?)\)/);
  // 
  console.log(args);
  // if the form arg is 'bB', then exec it, otherwise, do nothing
  for (var i = 0; i < args.length; i++) {
    if(args[i] == 'bB') {
      fn(bB);
    }
  }
}
// will do nothing
a('sdfdfdfs,', function(some){
alert(some)
});
// will alert 5

a('sdfdsdsfdfsdfdsf,', function(bB){
alert(bB)
});

// see, this shows you how to get function args in string
1

а затем из них собираются имена параметров, что позволяет ему поддерживать сопоставление с образцом, аргументы по умолчанию, функции стрелок и другие функции ES6.

https://www.npmjs.com/package/es-arguments

6

но так и не нашел практического способа сделать это. Я закончил тем, что передал объект вместо этого и затем проходил через него.

//define like
function test(args) {
    for(var item in args) {
        alert(item);
        alert(args[item]);
    }
}

//then used like
test({
    name:"Joe",
    age:40,
    admin:bool
});
Этот ответ не является ответом на первоначальный вопрос, который был определен точно. Это показывает решение для совершенно другой проблемы. Первоначальный вопрос относится к технике AngularJS, которая использует инъекцию зависимостей. Имена аргументов имеют смысл, поскольку они соответствуют зависимостям модуля, которые DI автоматически предоставляет.
У меня уже есть много предопределенных функций, которые вызываются со стандартными параметрами вместо одного объекта. Изменение всего займет много времени. vikasde
0

если вы хотите использовать деструктуризацию параметров ES6 в верхнем решении, добавьте следующую строку.

if (result[0] === '{' && result[result.length - 1 === '}']) result = result.slice(1, -1)
4

и я хотел бы добавить свою однострочную.

new RegExp(Function.name+'\\s*\\((.*?)\\)').exec(Function.toString().replace(/\n/g, ''))[1].replace(/\/\*.*?\*\//g, '').replace(/ /g, '')

или же

function getParameters(func) {
  return new RegExp(func.name+'\\s*\\((.*?)\\)').exec(func.toString().replace(/\n/g, ''))[1].replace(/\/\*.*?\*\//g, '').replace(/ /g, '');
}

или для однострочной функции в ECMA6

var getParameters = func => new RegExp(func.name+'\\s*\\((.*?)\\)').exec(func.toString().replace(/\n/g, ''))[1].replace(/\/\*.*?\*\//g, '').replace(/ /g, '');

__

Допустим, у вас есть функция

function foo(abc, def, ghi, jkl) {
  //code
}

Код ниже вернется"abc,def,ghi,jkl"

Этот код также будет работать с настройкой функции, котораяКамило Мартин дал:

function  (  A,  b
,c      ,d
){}

Также с комментариями Буберссона кДжек Аллан ответ:

function(a /* fooled you)*/,b){}

__

Explanation new RegExp(Function.name+'\\s*\\((.*?)\\)')

Это создаетОбычный экспонент сnew RegExp(Function.name+'\\s*\\((.*?)\\)'), Я должен использоватьnew RegExp потому что я вводить переменную (Function.nameимя целевой функции) в RegExp.

Example Если имя функции - "foo" (function foo()), RegExp будет/foo\s*\((.*?)\)/.

Function.toString().replace(/\n/g, '')

Затем он преобразует всю функцию в строку и удаляет все символы новой строки. Удаление новых строк помогает с настройкой функцииКамило Мартин дал.

.exec(...)[1]

ЭтоRegExp.prototype.exec функция. Это в основном соответствует обычному показателю (new RegExp()) в строку (Function.toString()). Тогда[1] вернет первыйCapture Group найдено в регулярном экспоненте ((.*?)).

.replace(/\/\*.*?\*\//g, '').replace(/ /g, '')

Это удалит каждый комментарий внутри/* а также*/и удалите все пробелы.

Если вы хотите превратить все параметры в массив вместо строки, разделенной запятыми, в конце просто добавьте.split(',').

1

AngularJS который реализует механизм внедрения зависимостей для работы без Angular. Я также обновилSTRIP_COMMENTS регулярное выражение для работы сECMA6, поэтому он поддерживает такие вещи, как значения по умолчанию в подписи.

var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
var FN_ARG_SPLIT = /,/;
var FN_ARG = /^\s*(_?)(.+?)\1\s*$/;
var STRIP_COMMENTS = /(\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s*=[^,\)]*(('(?:\\'|[^'\r\n])*')|("(?:\\"|[^"\r\n])*"))|(\s*=[^,\)]*))/mg;

function annotate(fn) {
  var $inject,
    fnText,
    argDecl,
    last;

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

console.log("function(a, b)",annotate(function(a, b) {
  console.log(a, b, c, d)
}))
console.log("function(a, b = 0, /*c,*/ d)",annotate(function(a, b = 0, /*c,*/ d) {
  console.log(a, b, c, d)
}))
annotate({})

2

Принимаяответ from @ jack-allan Я немного изменил функцию, чтобы включить свойства по умолчанию ES6, такие как:

function( a, b = 1, c ){};

чтобы все еще вернуться[ 'a', 'b' ]

/**
 * Get the keys of the paramaters of a function.
 *
 * @param {function} method  Function to get parameter keys for
 * @return {array}
 */
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
var ARGUMENT_NAMES = /(?:^|,)\s*([^\s,=]+)/g;
function getFunctionParameters ( func ) {
    var fnStr = func.toString().replace(STRIP_COMMENTS, '');
    var argsList = fnStr.slice(fnStr.indexOf('(')+1, fnStr.indexOf(')'));
    var result = argsList.match( ARGUMENT_NAMES );

    if(result === null) {
        return [];
    }
    else {
        var stripped = [];
        for ( var i = 0; i < result.length; i++  ) {
            stripped.push( result[i].replace(/[\s,]/g, '') );
        }
        return stripped;
    }
}
1

function name(arg1, arg2){
    var args = arguments; // array: [arg1, arg2]
    var objecArgOne = args[0].one;
}
name({one: "1", two: "2"}, "string");

Вы можете даже ссылаться на аргументы по имени функции, например:

name.arguments;

Надеюсь это поможет!

Ах ... Ты хочешь сказать, что хочешь в хэш-форме? Как будто:var args = name.arguments; console.log('I WANNa SEE', args); выведите что-то вроде & quot; {arg1: {...}, arg2: & quot;} & quot ;? Это может прояснить ситуацию:(function fn (arg, argg, arrrrgggg) { console.log('#fn:', fn.arguments, Object.keys(fn.arguments)); }); fn('Huh...?', 'Wha...?', 'Magic...?');, Аргументы функций являются объектами, подобными «массивам», имеющими перечисляемые индексы. Я не думаю, что хеш-отображение возможно, но вы могли бы просто передать Object-литерал, что является хорошей практикой, если у вас в любом случае более 4 параметров.
А где имена параметров функций?
0

function test(arg1,arg2){
    var funcStr = test.toString()
    var leftIndex = funcStr.indexOf('(');
    var rightIndex = funcStr.indexOf(')');
    var paramStr = funcStr.substr(leftIndex+1,rightIndex-leftIndex-1);
    var params = paramStr.split(',');
    for(param of params){
        console.log(param);   // arg1,arg2
    }
}

test();
Этот пример просто для получения параметров & apos; имя для вас.
Этот ответ полезен, но он не отвечает на вопрос. Я проголосовал, потому что это решило мою проблему, но я думаю, что это должно быть перемещено куда-то более подходящее. Возможно поискать похожие вопросы?
118

взятый из AngularJS, который использует технику для механизма внедрения зависимостей.

И вот объяснение этого взято изhttp://docs.angularjs.org/tutorial/step_05

Angular's dependency injector provides services to your controller when the controller is being constructed. The dependency injector also takes care of creating any transitive dependencies the service may have (services often depend upon other services).

Note that the names of arguments are significant, because the injector uses these to look up the dependencies.

/**
 * @ngdoc overview
 * @name AUTO
 * @description
 *
 * Implicit module which gets automatically added to each {@link AUTO.$injector $injector}.
 */

var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
var FN_ARG_SPLIT = /,/;
var FN_ARG = /^\s*(_?)(.+?)\1\s*$/;
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
function annotate(fn) {
  var $inject,
      fnText,
      argDecl,
      last;

  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;
    }
  } else if (isArray(fn)) {
    last = fn.length - 1;
    assertArgFn(fn[last], 'fn')
    $inject = fn.slice(0, last);
  } else {
    assertArgFn(fn, 'fn', true);
  }
  return $inject;
}
@ sasha.sochka, пришла сюда, удивляясь точно так же, поняв, что не было встроенного способа получить имена параметров с помощью javascript
Если вы уже используете Angular, вы можете использовать его$injector service: $injector.invoke(function(serviceA){});
@apaidnerd с кровью демонов и порождением сатаны, очевидно. Regex ?! Было бы здорово, если бы в JS был встроенный способ, не так ли?
чтобы сэкономить время людей, вы можете получить эту функцию из углового черезannotate = angular.injector.$$annotate
@apaidnerd, это правда! Просто подумал - как, черт возьми, это реализовано? На самом деле я думал об использовании functionName.toString (), но я надеялся на что-то более элегантное (и, возможно, быстрее)
-1

function parameter string value image dynamically from JSON, Поскольку item.product_image2 является строкой URL, вам нужно поместить ее в кавычки, когда вы вызываете changeImage внутри параметра.

Моя функция Onclick

items+='<img src='+item.product_image1+' id="saleDetailDivGetImg">';
items+="<img src="+item.product_image2+"  onclick='changeImage(\""+item.product_image2+"\");'>";

Моя функция

<script type="text/javascript">
function changeImage(img)
 {
    document.getElementById("saleDetailDivGetImg").src=img;
    alert(img);
}
</script>

Похожие вопросы