함수 매개 변수 이름 / 값을 동적으로 얻는 방법?


300

함수의 함수 매개 변수 이름을 동적으로 얻는 방법이 있습니까?

내 기능이 다음과 같다고 가정 해 봅시다.

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

이제 함수 내부에서 매개 변수 이름과 값 목록을 배열로 가져 오는 방법은 무엇입니까?


모두에게 감사합니다. 주변 검색 후, 나는 SO에 대한 해결책을 발견 : stackoverflow.com/questions/914968/...은 그것은 매개 변수 이름을 얻기 위해 정규식을 사용합니다. 아마도 최선의 해결책은 아니지만 나에게 효과적입니다.
vikasde 2016 년

8
계속해서 대답 한 친구로 표시합니다. 새로운 답변이 오지 않습니다.
Matthew Graves

function doSomething(...args) { /*use args*/}
caub

답변:


322

다음 함수는 전달 된 함수의 매개 변수 이름 배열을 반환합니다.

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 []

편집 :

ES6의 발명으로이 기능은 기본 매개 변수에 의해 넘어 질 수 있습니다. 다음은 대부분의 경우 작동해야하는 빠른 해킹입니다.

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

나는 그것을 트립 할 것들이 있기 때문에 대부분의 경우를 말한다

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

편집 : 또한 vikasde는 배열의 매개 변수 값도 원한다는 점에 유의하십시오. 이것은 이미 arguments라는 로컬 변수로 제공됩니다.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments 에서 발췌 :

arguments 객체는 Array가 아닙니다. Array와 비슷하지만 length를 제외한 Array 속성이 없습니다. 예를 들어, pop 메소드가 없습니다. 그러나 실제 배열로 변환 할 수 있습니다.

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

배열 제네릭을 사용할 수있는 경우 다음을 대신 사용할 수 있습니다.

var args = Array.slice(arguments);

12
이 솔루션 때문에 의견과 공간의 실패 할 수 있습니다 - 예를 들어이 : var fn = function(a /* fooled you)*/,b){};발생합니다 ["a", "/*", "fooled", "you"]
bubersson

1
인수가 없을 때 빈 배열을 반환하도록 함수를 수정했습니다.
BT

2
정규식을 컴파일하는 비용이 있으므로 복잡한 정규식을 두 번 이상 컴파일하지 않으려 고합니다. 이것이 기능 외부에서 수행되는 이유입니다.
Jack Allan

2
수정 : perl이 허용하는 / s 수정자를 사용하여 정규식을 수정하려고했습니다. 개행과도 일치 할 수 있습니다. 이것은 / * * / 내의 여러 줄 주석에 필요합니다. Javascript 정규식에서 / s 수정자를 허용하지 않습니다. [/ s / S]를 사용하는 원래 정규식은 줄 바꿈 문자와 일치합니다. SOOO, 이전 의견을 무시하십시오.
tgoneil

1
@andes 테스트에 정규식 컴파일을 포함시킵니다. 이 작업은 한 번만 수행해야합니다. 정규식 컴파일을 테스트 대신 설정 단계로 옮기면 결과가 다를 수 있습니다.
Jack Allan

123

다음은 의존성 주입 메커니즘에 대한 기술을 사용하는 AngularJS에서 가져온 코드입니다.

다음은 http://docs.angularjs.org/tutorial/step_05 에서 가져온 설명입니다.

Angular의 의존성 인젝터는 컨트롤러가 구성 될 때 컨트롤러에 서비스를 제공합니다. 의존성 인젝터는 또한 서비스가 가질 수있는 전이 의존성을 생성하는 것을 관리합니다 (서비스는 종종 다른 서비스에 의존합니다).

인젝터는이를 사용하여 종속성을 조회하므로 인수 이름이 중요합니다.

/**
 * @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;
}

40
@apaidnerd 악마의 피와 사탄의 산란, 분명히. 정규식?! JS에 내장 된 방식이 있다면 멋질 것입니다.
Aditya MP

6
@apaidnerd, 너무 사실이야! 그냥 생각-어떻게 구현되어 있습니까? 실제로 functionName.toString () 사용에 대해 생각했지만 더 우아하고 더 빠른 것을 원했습니다.
sasha.sochka

2
@ sasha.sochka, 똑같은 일을 궁금해 여기 온 더 자바 스크립트로 매개 변수 이름을 얻을 방법이 지어진 실현되지 후
하트 Simha

14
사람들의 시간을 절약하기 위해 각도를 통해이 기능을 사용할 수 있습니다. annotate = angular.injector.$$annotate
Nick

3
나는 Angular가 어떻게했는지 궁금해하기 때문에 말 그대로 인터넷 에서이 주제를 검색했습니다 ... 지금은 알고 있고 너무 많이 알고 있습니다!
Steven Hunt

40

다음은 위에서 언급 한 모든 주요 사례를 간결하게 해결하려는 업데이트 된 솔루션입니다.

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 [""]
}  

약식 테스트 결과 (전체 테스트 사례는 아래에 첨부 됨) :

'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 []


한 줄 주석이 있으면 중단됩니다. return (func+'') .replace(/[/][/].*$/mg,'') // strip single-line comments (line-ending sensitive, so goes first) .replace(/\s+/g,'') // remove whitespace
Merlyn Morgan-Graham

4
함수에 사용자 정의 .toString () 구현이있는 경우를 방지하기 위해 func + ''로 대체해야합니다 Function.toString.call(func).
Paul Go

1
팻 화살표 =>.split(/\)[\{=]/, 1)[0]
Matt

1
이렇게하면 (와 같은 ({ a, b, c })) 비 구조화 된 객체가 구조 해제 내의 모든 매개 변수로 분할 됩니다. 변형 된 객체를 그대로 유지하려면 마지막 .split을 다음 과 같이 변경하십시오 . .split(/,(?![^{]*})/g)
Michael Auderer

1
"//"또는 "/ *"를 포함하는 기본 문자열 값이있는 경우에도 작동하지 않습니다.
skerit

23

공백과 주석이 발생하기 쉬운 오류가 적은 솔루션은 다음과 같습니다.

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

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

["hi", "you"]

1
@AlexMills 내가 주목 한 것은 Arrow Functions에 대한 스펙이 '함수'로 취급되어서는 안된다는 것입니다. 이것은 배열 함수와 일치하는 것이 적절하지 않다는 것을 의미합니다. 'this'는 같은 방식으로 설정되지 않았으며 함수로도 호출되어서는 안됩니다. 내가 힘들게 배운 것입니다. ($ myService) => $ myService.doSomething ()은 멋지지만 배열 함수의 오용입니다.
Andrew T Finnell

20

여기에 대한 많은 대답은 정규 표현식을 사용합니다. 이것은 훌륭하지만 화살표 기능 및 클래스와 같이 언어에 대한 새로운 추가 기능을 너무 잘 처리하지 못합니다. 또한 축소 된 코드에서 이러한 함수를 사용하면 🔥으로 이동합니다. 축소 된 이름이 무엇이든 사용합니다. 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);
        }
    }
}

그것은 가장 진보 된 리졸버는 아니지만 간단한 DI에 args 파서를 사용하려는 경우 프록시를 사용하여 프록시를 처리하는 방법에 대한 아이디어를 제공합니다. 그러나이 방법에는 약간의 경고가 있습니다. 일반적인 매개 변수 대신 파괴적인 할당을 사용해야합니다. 인젝터 프록시를 전달할 때, 파괴는 객체에서 getter를 호출하는 것과 같습니다.

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 등을 전달할 수 있습니다). 또한 구현을 교체해야하는 경우 한 곳에서 구현할 수 있습니다. JS 프록시 객체로 인해이 모든 것이 가능합니다.

참고 : 프로덕션 용도로 사용하기 전에이 작업을 수행해야하는 작업이 많이 있지만 모양에 대한 아이디어를 제공합니다.

답이 늦었지만 같은 것을 생각하는 다른 사람들에게 도움이 될 수 있습니다. 👍


13

나는 이것이 오래된 질문이라는 것을 알고 있지만 초보자는 이것이 어떤 코드에서나 좋은 습관 인 것처럼 이것을 복사하여 붙여 넣었습니다. 대부분의 경우 매개 변수 이름을 사용하기 위해 함수의 문자열 표현을 구문 분석해야 코드 논리의 결함이 숨겨집니다.

함수의 매개 변수는 실제로라는 배열 형 객체에 저장됩니다. 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"만 표시됩니다. 전혀 유용하지 않습니다.


1
필자는 일반적으로 디버깅 / 로깅, 펑키 한 물건을 만드는 일종의 데코레이터 (기술 용어 😁) 또는 인수 이름을 기반으로 앱이 자동으로 주입 할 수있는 의존성 주입 프레임 워크를 작성하는 것입니다 (각도의 각도입니다) 공장). 또 다른 흥미로운 유스 케이스는 promisify-node에 있습니다 (일반적으로 콜백을 수행하고이를 Promise로 변환하는 함수를 취하는 라이브러리입니다). 콜백의 공통 이름 (예 : cb / callback / etc)을 찾기 위해이 이름을 사용한 다음 랩핑하기 전에 함수가 비동기인지 동기화되는지 확인할 수 있습니다.
James Drew

파서에 대해서는 이 파일을 참조하십시오 . 약간 순진하지만 대부분의 경우를 처리합니다.
James Drew

흥미롭게도, 그런 도서관에 관심이 있다는 것이 놀랍습니다. 글쎄, 내가 설명한 것과 같은 문제가있는 상황에 대해 여러 가지 공개 된 문제가 있습니다. 내가 말했듯이, 디버깅 목적이라면 괜찮지 만 프로덕션 환경에서 함수의 문자열 변환에 의존하는 것은 너무 위험합니다.
Domino

"재미있는"부가 정보에서 다음을 실행하여 모든 기능이 익명 내장 함수처럼 작동하도록 할 수 있습니다.Function.prototype.toString = function () { return 'function () { [native code] }'; };
Domino

1
동의합니다, 이렇게 처리하는 것은 약간 지저분합니다. 가장 쉽고 직접적인 사용은 의존성 주입입니다. 이 경우 코드를 소유하고 이름 지정 및 기타 코드 영역을 처리 할 수 ​​있습니다. 이것이 가장 많이 사용되는 곳이라고 생각합니다. 현재 정규 표현식 대신 esprima 및 ES6 Proxy( 생성자 트랩적용 트랩 )을 사용하고 Reflection일부 모듈의 DI를 처리합니다. 합리적으로 견고합니다.
James Drew

10

JavaScript는 스크립팅 언어이기 때문에, 그 내성 (introspection)이 함수 매개 변수 이름을 얻는 것을 지원해야한다고 생각합니다. 이 기능을 사용하는 것은 첫 번째 원칙을 위반하는 것이기 때문에 문제를 더 조사하기로 결정했습니다.

그로 인해이 질문으로 이어 졌지만 기본 제공 솔루션은 없습니다. 저를지도 한 이 답 이라고 설명 arguments에만 사용되지 않습니다 외부 그래서 우리는 더 이상 사용할 수있는 함수 myFunction.arguments또는 우리가 얻을 :

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

소매를 감아 일할 시간 :

⭐ 함수 매개 변수를 검색하려면 구문 분석기와 같은 복잡한 표현식 4*(5/3)을 기본값으로 사용할 수 있으므로 구문 분석기가 필요 합니다. 그래서 Gaafar의 대답 이나 제임스 드류의 답변 이 지금까지 가장 좋은 방법입니다.

나는 바빌론에스프리 마를 시도 파서를 했지만 불행히도 Mateusz Charytoniuk의 대답 에서 지적했듯이 독립형 익명 함수를 구문 분석 할 수 없습니다 . 논리를 변경하지 않도록 코드를 괄호로 묶어 다른 해결 방법을 찾았습니다.

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

//줄 바꿈은 (한 줄 주석) 문제를 방지합니다 .

⭐ 파서를 사용할 수 없다면, 차선책은 Angular.js의 의존성 인젝터 정규 표현식과 같은 검증 된 기술을 사용하는 것입니다. 나는의 기능 버전 결합 Lambder의 답변을 함께 humbletim의 대답 하고 옵션 추가 ARROWES6 지방 화살표 기능은 정규 표현식으로 사용할 수 있는지 여부를 제어하는 부울.


다음은 두 가지 솔루션입니다. 이것에는 함수에 유효한 구문이 있는지 여부를 감지하는 논리가 없으며 인수 만 추출합니다. 일반적으로 파싱 된 함수를 전달하기 때문에 일반적으로 괜찮습니다.getArguments() 하여 구문이 이미 유효 때문에 .

최선을 다해 이러한 솔루션을 선별하려고 노력하지만, JavaScript 관리자의 노력 없이는 공개적인 문제로 남아있을 것입니다.

Node.js 버전 (StackOverflow가 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

브라우저 버전 (첫 번째 복잡한 기본값에서 중지됨) :

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의 첫 번째 조건은 화살표와 전통적인 함수 모두에서 작동하므로 두 번째 부분이 필요하지 않으므로 화살표에 대한 종속성을 제거 할 수 있습니다.
pwilcox

대단해! ES6 구문을 다루기 위해 파서를 사용하는 이와 같은 솔루션을 찾고있었습니다. 단순히 function.length를 사용하면 기본 매개 변수에 제한이 있으며 나머지 매개 변수를 주장 할 수 있기 때문에 이것을 사용하여 jest "구현 인터페이스"매처를 만들 계획입니다.
cue8chalk

기본값에 괄호를 포함하는 다섯 번째 테스트 사례가 현재 실패했음을 지적 할 가치가 있습니다. 내 정규식이 고칠만큼 강했으면 좋겠다. 죄송합니다!
데일 앤더슨

8

나는 대부분의 답변을 읽었으며 내 라이너를 추가하고 싶습니다.

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*|^)\\s*\\((.*?)\\)').exec(func.toString().replace(/\n/g, ''))[1].replace(/\/\*.*?\*\//g, '').replace(/ /g, '');
}

또는 ECMA6의 단일 라이너 기능

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

__

기능이 있다고 가정 해 봅시다

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

아래 코드는 "abc,def,ghi,jkl"

이 코드는 Camilo Martin이 제공 한 기능 설정과 함께 작동합니다 .

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

또한 Jack Allan의 답변에 대한 Bubersson의 의견과 함께 :

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

__

설명

new RegExp('(?:'+Function.name+'\\s*|^)\\s*\\((.*?)\\)')

정규 표현식 을 만듭니다 new RegExp('(?:'+Function.name+'\\s*|^)\\s*\\((.*?)\\)'). new RegExp변수를 주입 하기 때문에 사용해야 합니다 (Function.nameRegExp에 , 대상 함수의 이름)를 합니다.

함수 이름이 "foo"( function foo())이면 RegExp는입니다 /foo\s*\((.*?)\)/.

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

그런 다음 전체 함수를 문자열로 변환하고 모든 줄 바꿈을 제거합니다. 줄 바꾸기를 제거하면 Camilo Martin이 제공 한 기능 설정에 도움이됩니다 .

.exec(...)[1]

이것이 RegExp.prototype.exec기능입니다. 기본적으로 정규 지수 ( new RegExp())를 문자열 ( Function.toString())에 일치시킵니다 . 그러면 정규 지수 ( ) 에서 찾은 [1]첫 번째 캡처 그룹 이 반환됩니다 (.*?).

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

그러면 /*및 안의 모든 주석이 제거되고 */모든 공백이 제거됩니다.


또한 이제는 일반 함수 대신에 반환 되는 화살표 ( =>) 함수를 읽고 이해하는 기능 도 지원합니다 . 원래 정규 표현식은 혼동에 오류가 발생했지만 이제는 설명됩니다.f = (a, b) => void 0;Function.toString()(a, b) => void 0function f(a, b) { return void 0; }

변화는 출신 new RegExp(Function.name+'\\s*\\((.*?)\\)')( /Function\s*\((.*?)\)/에) new RegExp('(?:'+Function.name+'\\s*|^)\\((.*?)\\)')( /(?:Function\s*|^)\((.*?)\)/)


쉼표로 구분 된 문자열 대신 모든 매개 변수를 배열로 만들려면 끝에 추가하십시오 .split(',').


꽤 좋아요. 한 줄로, 화살표 기능을 처리하고 외부 종속성을 처리하지 않으며 적어도 내가 의도 한 용도로 사용하기에 충분한 양보다 많은 경우를 처리합니다. 함수 시그니처에 대해 합리적인 가정을 할 수 있다면 이것 만 다룰 것입니다. 감사!
찰리 로버츠

이 간단한 화살표 기능으로는 작동하지 않습니다. f = (a, b) => void 0; 에 getParameters(f)내가 얻을TypeError: Cannot read property '1' of null
AT

@AT 방금 문제에 대한 지원을 해결하기 위해 답변을 업데이트했습니다.
Jaketr00

당신이 일을 할 수 있도록처럼 덕분에 ...하지만, 괄호가 더 이상 필요하지 않은 것을 명심없는 getParameters(a => b => c => d => a*b*c*d)코드로 여전히를 제공하는 TypeError: Cannot read property '1' of null이 하나 개의 작품 반면 ... stackoverflow.com/a/29123804
AT

함수에 기본값 (역할, name = "bob")이있는 경우 작동하지 않습니다. 추출 된 매개 변수는 "name"이 아닌 name = "bob"입니다.
Dmitri

7
(function(a,b,c){}).toString().replace(/.*\(|\).*/ig,"").split(',')

=> [ "a", "b", "c"]


7

"esprima"구문 분석기를 사용하여 주석, 공백 및 매개 변수 목록 내의 다른 항목과 관련된 많은 문제를 피할 수 있습니다.

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"]

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

단일 객체 대신 표준 매개 변수로 호출되는 미리 정의 된 많은 함수가 있어야합니다. 모든 것을 바꾸려면 많은 시간이 걸릴 것입니다.
vikasde 2016 년

2
이 답변은 정확하게 정의 된 원래 질문에 대한 답변이 아닙니다. 완전히 다른 문제에 대한 해결책을 보여줍니다. 원래의 질문은 AngularJS가 의존성 주입을 위해 사용하는 기술을 말합니다. 인수 이름은 DI가 자동으로 제공하는 모듈의 종속성에 해당하므로 의미가 있습니다.
Lambder

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>

도움이 되길 바랍니다.


4

이를 수행하는 올바른 방법은 JS 파서를 사용하는 것입니다. 다음은 acorn을 사용한 예 입니다.

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().


값은 어떻습니까?
eran otzap

2

매개 변수 목록을 얻는 방법을 모르지만 원하는 개수를 얻으려면이 작업을 수행 할 수 있습니다.

alert(doSomething.length);

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

2

복용 대답을 하는 기능이 약간 같은 ES6의 기본 속성을 허용하는 잭 - 앨런 I @에서 수정 된 항목 :

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
고마워요, 당신의 스레드가 나를 위해 일한 유일한 스레드입니다.
AT

1

내가 일반적으로하는 방법 :

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

다음과 같은 함수 이름으로 인수를 참조 할 수도 있습니다.

name.arguments;

도움이 되었기를 바랍니다!


4
그리고 함수 매개 변수의 이름은 어디에 있습니까?
Andrej

아 .. 해시 형태로 원하시나요? : 마치 var args = name.arguments; console.log('I WANNa SEE', args);같은 출력 뭔가 "{ARG1 : {...}, ARG2 : '문자열'}"? 이렇게하면 문제가 해결 될 수 있습니다 (function fn (arg, argg, arrrrgggg) { console.log('#fn:', fn.arguments, Object.keys(fn.arguments)); }); fn('Huh...?', 'Wha...?', 'Magic...?');.. 함수 인수는 열거 가능한 인덱스를 갖는 '배열'과 유사한 객체입니다. 나는 해시 매핑이 가능하다고 생각하지 않지만 어쨌든 4 개 이상의 매개 변수가있는 경우 모범 사례 인 Object-literal을 전달할 수 있습니다.
Cody

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

이에 대한 답은 3 단계가 필요합니다.

  1. 함수에 전달 된 실제 매개 변수의 값을 가져 오려면 (호출 argValues) 이것은 다음과 같이 사용할 수 있으므로 간단합니다.arguments 함수 내부에서 합니다.
  2. 함수 서명에서 매개 변수 이름을 가져 오려면 (호출 argNames) 이것은 쉽지 않으며 함수 구문 분석이 필요합니다. 복잡한 정규 표현식을 직접 수행하고 가장자리 사례 (기본 매개 변수, 주석 등)에 대해 걱정하는 대신 함수를 추상 구문 트리로 구문 분석하여 매개 변수 이름을 얻을 수있는 바빌론과 같은 라이브러리를 사용할 수 있습니다.
  3. 마지막 단계는 2 개의 배열을 모든 매개 변수의 이름과 값을 가진 1 개의 배열로 결합하는 것입니다.

코드는 다음과 같습니다

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


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

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\'' ] ]

알 수 있듯이 일부 매개 변수 이름은 바벨 트랜스 파일러가 함수에서 제거하기 때문에 사라집니다. 최신 NodeJS에서 이것을 실행하면 예상대로 작동합니다 (주석 결과는 NodeJS에서 온 것입니다).

주석에 언급 된 또 다른 메모는 인라인 화살표 기능을 기본값으로 사용할 수 없다는 것입니다. 이것은 단순히 RegExp를 사용하여 값을 추출하는 것을 복잡하게 만듭니다.

이것이 당신에게 유용한 지 알려주세요! 의견을 듣고 싶습니다!


1

아래에 간단한 예를 들어 보겠습니다.

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();

이 예제는 매개 변수 이름을 얻는 것입니다.
myzhou

이 답변은 유용하지만 질문에 대한 답변은 아닙니다. 문제를 해결했기 때문에 투표를했지만 더 적절한 곳으로 옮겨야한다고 생각합니다. 관련 질문을 검색 하시겠습니까?
chharvey


1

Angular없이 작동하도록 종속성 주입 메커니즘을 구현하는 AngularJS 에서 가져온 버전을 수정했습니다 . 또한 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({})


0

"arguments"속성을 사용하여 함수에 전달 된 인수 값에 액세스 할 수 있습니다.

    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]);    

인수가 더 이상 사용되지 않습니까? 위의 Ionut G. Stan의 제안을 참조하십시오.
vikasde 2016 년

vikasde가 맞습니다. arguments함수 인스턴스 의 속성에 액세스하는 것은 더 이상 사용되지 않습니다. developer.mozilla.org/en/Core_JavaScript_1.5_Reference/…
Ionuț G. Stan

0

꽤 쉽습니다.

처음에는 더 이상 사용되지 않는 arguments.callee-호출 된 함수에 대한 참조가 있습니다. 두 번째로 함수에 대한 참조가 있으면 텍스트 표현을 쉽게 얻을 수 있습니다. 세 번째에서 생성자로 함수를 호출하면 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 : Jack Allan은 실제로 약간 더 나은 솔루션을 제공했습니다. GJ 잭!


(함수 객체 자체를 가리키는 SomeFuncName대신) 대신 사용하면 훨씬 더 간단 할 수 있습니다 arguments.callee.
Raphael Schweikert 2016 년

0

솔루션이 무엇이든간에 Wierd와 같은 Wierd 기능을 중단해서는 안됩니다 toString().

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

콘솔 스크린 샷

또한 왜 복잡한 정규 표현식을 사용합니까? 이것은 다음과 같이 수행 할 수 있습니다 :

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

이것은 모든 함수와 함께 모든 곳에서 작동하며 유일한 정규 표현식은 공백으로 제거하여 .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);

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.


2
while 루프는 제공된 코드를 사용하여 무한 루프를 만듭니다 (20171121at1047EDT 기준)
George 2.0 Hope

@ George2.0Hope 지적 해 주셔서 감사합니다. 답변을 업데이트하겠습니다.
Ates Goral

args.toSource는 함수가 아닙니다 (라인 20). 그러나 변경 한 경우에도 다음과 같이 변경됩니다. console.log (args.toString ()); ... 당신은 ... [객체 객체]를 얻습니다 ... 더 잘하면 : console.log (JSON.stringify (args));
George 2.0 Hope

2009 년 이후 많은 것이 바뀌 었습니다!
Ates Goral

1
누군가 React를 위해 여기에 표시되는 경우이 기능은 엄밀한 모드에서 작동하지 않습니다.
개인 11

0

참고 : 최상위 솔루션으로 ES6 매개 변수 파괴를 사용하려면 다음 줄을 추가하십시오.

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

-1

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>
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.