関数のパラメーター名/値を動的に取得するにはどうすればよいですか?


301

関数の関数パラメーター名を動的に取得する方法はありますか?

私の関数が次のようになっているとしましょう:

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

では、関数の内部からパラメーター名とその値のリストを配列に取得するにはどうすればよいでしょうか。


みんなありがとう。周りを検索した後、SOで解決策を見つけました:stackoverflow.com/questions/914968/…正規表現を使用してパラメーター名を取得します。それはおそらく最善の解決策ではありませんが、私にとってはうまくいきます。
vikasde 2009年

8
先に進み、これを回答済みのバディとしてマークします。新しい答えは来ていません。
マシューグレイブス2013

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

回答:


323

次の関数は、渡された関数のパラメーター名の配列を返します。

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からの抜粋:

引数オブジェクトは配列ではありません。これは配列に似ていますが、長さを除いて配列プロパティはありません。たとえば、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
引数がないときに空の配列(nullではなく)を返すように関数を変更しました
BT

2
正規表現のコンパイルにはコストがかかるため、複雑な正規表現を2回以上コンパイルしないようにします。これが関数の外で行われる理由です
ジャックアラン

2
修正:perlが許可する/ s修飾子を使用して正規表現を変更しようとしていました。改行も一致できます。これは、/ * * /内の複数行コメントに必要です。Javascriptの正規表現では/ s修飾子を使用できないことがわかりました。[/ s / S]を使用する元の正規表現は、改行文字と一致します。前回のコメントは無視してください。
tgoneil 2014年

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 '28

2
@ sasha.sochkaは、javascriptを使用してパラメーター名を取得する組み込みの方法がないことに気づいた後、まったく同じことを疑問に思ってここに来ました
Hart Simha

14
人々の時間を節約するために、この機能をangular viaから入手できます annotate = angular.injector.$$annotate
Nick

3
Angularのやり方に興味があったので、文字通りインターネットでこのトピックを検索しました。
スティーブンハント

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


これは、1行のコメントが存在すると壊れます。これを試してください: return (func+'') .replace(/[/][/].*$/mg,'') // strip single-line comments (line-ending sensitive, so goes first) .replace(/\s+/g,'') // remove whitespace
Merlyn Morgan-Graham 2016

4
おそらく、交換する必要がありますfunc + ''Function.toString.call(func)機能は、カスタム.toString()の実装を持っている場合を防御します。
Paul Go

1
fat arrows =>.split(/\)[\{=]/, 1)[0]
Matt

1
これにより、非構造化オブジェクト(など({ a, b, c }))が構造化解除内のすべてのパラメーターに分割されます。そのまま非構造オブジェクトを保持するために、最後の変更.split:に .split(/,(?![^{]*})/g)
マイケル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私が気づいたことの1つは、矢印関数の仕様が「関数」として扱われるべきではないと述べていることです。つまり、これが配列関数と一致するのは適切ではありません。'this'は同じように設定されていないため、関数として呼び出すこともできません。それは私が苦労して学んだことでした。($ myService)=> $ myService.doSomething()はクールに見えますが、これは配列関数の誤用です。
Andrew T Finnell 2016

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パーサーを使用する場合に、プロキシーを使用してそれを処理する方法についてのアイデアを提供します。ただし、このアプローチには1つ注意点があります。通常のパラメータの代わりに、構造化代入を使用する必要があります。インジェクタプロキシを渡すと、構造化はオブジェクトのゲッターを呼び出すのと同じです。

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": {}
        }
    }
}

その全体のアプリケーションを配線しました。最善の点は、アプリのテストが簡単であることです(各クラスをインスタンス化して、モック/スタブなどに渡すことができます)。また、実装を入れ替える必要がある場合は、単一の場所から行うことができます。JSプロキシオブジェクトにより、これらすべてが可能です。

注:本番環境で使用する前に、これまでに行う必要のある多くの作業がありますが、実際にどのように見えるかはわかります。

答えは少し遅いですが、同じことを考えている他の人を助けるかもしれません。👍


13

私はこれが古い質問であることを知っていますが、初心者はこれがどのコードでも良い習慣であるかのようにコピーペーストしてきました。ほとんどの場合、パラメータ名を使用するために関数の文字列表現を解析する必要があることは、コードのロジックの欠陥を隠すだけです。

関数のパラメータは、実際にはと呼ばれる配列のようなオブジェクトに格納されます。arguments最初の引数はarguments[0]、2番目の引数はと続き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
私はこのようなもののケースが通常だと思います:デバッグ/ロギング、ファンキーなことを行うある種のデコレーター(専門用語😁)、または引数名に基づいてアプリが自動的に注入する依存性注入フレームワークの構築(これはどのように角度があるか)動作します)。もう1つの本当に興味深い使用例は、promisify-nodeです(通常はコールバックを受け取り、それをPromiseに変換する関数を使用するライブラリです)。これを使用して、コールバックの一般的な名前(cb / callback / etcなど)を見つけ、ラップする前に関数が非同期か同期かを確認できます。
James Drew

パーサーについては、このファイルを参照してください。それは少し素朴ですが、ほとんどの場合を処理します。
James Drew

興味深いことに、そのようなライブラリが注目を集めたことに驚いています。まあ、私が説明したような問題のある状況については、未解決の問題が複数あります。先ほど述べたように、デバッグ目的であれば問題ありませんが、本番環境で関数の文字列変換に依存するのは非常に危険です。
ドミノ

:「面白い」追記では、あなたは彼らがこれを実行することにより、匿名のインを建てたかのように、すべての機能が動作することができますFunction.prototype.toString = function () { return 'function () { [native code] }'; };
ドミノ

1
同意し、このように処理するのは少し面倒です。最善かつ最も簡単な使用法は、依存性注入です。その場合は、コードを所有していて、コードの命名やその他の領域を処理できます。私はこれが最も使用が見られる場所だと思います。イムは、現在使用してesprima(代わりに正規表現の)およびES6 Proxyコンストラクタトラップをしてトラップを適用)とReflection私のモジュールのいくつかのためにDIを処理します。かなりしっかりしています。
James Drew

10

JavaScriptはスクリプト言語なので、そのイントロスペクションは関数パラメーター名の取得をサポートするべきだと思います。その機能をパンティングすることは第一原則に違反するため、この問題をさらに調査することにしました。

それがこの質問につながりましたが、組み込みのソリューションはありませんでした。これは私がこの答えに導いたもので、これは関数の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

袖をまくって仕事に取り掛かる時間:

likeなどの複雑な式4*(5/3)をデフォルト値として使用できるため、関数パラメーターを取得するにはパーサーが必要です。したがって、これまでのところ、Gaafarの回答またはJames Drewの回答が最善のアプローチです。

Mateusz Charytoniukの回答で指摘されているように、バビロンエスプリマのパーサーを試してみましたが、残念ながらスタンドアロンの匿名関数を解析できません。ロジックを変更しないように、コードを括弧で囲んで別の回避策を見つけました。

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

改行は//(単一行コメント)の問題を防ぎます。

aパーサーが利用できない場合、次善の策は、Angular.jsの依存性インジェクター正規表現のような実証済みの手法を使用することです。Lambderの回答の機能バージョンとhumbletimの回答組み合わせ、オプションのARROWブール値を追加して、正規表現でES6ファットアロー関数を許可するかどうかを制御しました。


これが私がまとめた2つのソリューションです。これらには、関数に有効な構文があるかどうかを検出するロジックはなく、引数を抽出するだけであることに注意してください。通常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の最初の条件は矢印と従来の関数の両方で機能するため、2番目の部分は必要ないため、ARROWへの依存をなくすことができます。
pwilcox 2018

これは素晴らしい!ES6構文をカバーするためにパーサーを使用するこのようなソリューションを探していました。これを使用してjestの「実装インターフェース」マッチャーを作成することを計画しています。単純にfunction.lengthを使用すると、デフォルトのパラメーターに制限があり、残りのパラメーターをアサートできるようにしたかったからです。
cue8chalk 2018年

デフォルト値に括弧が含まれている5番目のテストケースが現在失敗していることを指摘する価値があります。私の正規表現fuが修正できるほど強かったといいのですが、ごめんなさい!
デールアンダーソン、

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

また、ジャックアランの回答に対する Buberssonのコメントを以下に示します

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

__

説明

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

これにより、で正規表現が作成されますnew RegExp('(?:'+Function.name+'\\s*|^)\\s*\\((.*?)\\)')new RegExp変数(Function.nameターゲットとなる関数の名前)をRegExp に注入しているので、使用する必要があります。

関数名が "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(',')ます。


けっこういい。1行で、矢印関数を処理し、外部依存関係はなく、少なくとも私の意図する用途では、十分なエッジケースをカバーします。関数のシグネチャについて妥当な仮定を行うことができれば、これを処理するだけで十分です。ありがとう!
チャーリーロバーツ

この単純な矢印関数では機能しません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

関数にデフォルト値(ロール、名前= "bob")がある場合は機能しません抽出されたパラメーターは、予期される "名前"ではなく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 2009年

2
この回答は、正確に定義された元の質問に対する回答ではありません。それは完全に異なる問題の解決策を示しています。元の質問は、AngularJSが依存関係の注入から使用する手法に言及しています。引数名は、DIが自動的に提供するモジュールの依存関係に対応するため、意味があります。
Lambder 2012

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

ここのコードは、関数の3つの(正式な)パラメータの名前を見つけますf。にフィードfすることでそうしacorn.parse()ます。


値はどうですか?
eran otzap

2

パラメータのリストを取得する方法はわかりませんが、これを実行して、必要な数を取得できます。

alert(doSomething.length);

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

2

撮影答えを:ジャック・アラン私のような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
ありがとう、このスレッドで私のために働いたのはあなただけです。
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 2013年

あ...ハッシュ形式で欲しいってこと?あたかも:var args = name.arguments; console.log('I WANNa SEE', args);"{arg1:{...}、arg2: 'string'}"のようなものを出力しますか?これにより、問題が解決する場合があります(function fn (arg, argg, arrrrgggg) { console.log('#fn:', fn.arguments, Object.keys(fn.arguments)); }); fn('Huh...?', 'Wha...?', 'Magic...?');。関数の引数は、列挙可能なインデックスを持つ「配列」のようなオブジェクトです。ハッシュマッピングは可能ではないと思いますが、いずれにしても4つ以上のパラメーターがある場合は、Object-literalを渡すだけで十分です。
コーディ

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)。これは簡単ではなく、関数の解析が必要です。複雑な正規表現を自分で実行してエッジケース(デフォルトのパラメーター、コメントなど)を心配する代わりに、関数を解析してパラメーターの名前を取得できる抽象構文ツリーに変換するbabylonのようなライブラリを使用できます。
  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ではデフォルト値ではうまく機能しないため、選択した回答に完全に満足していませんでした。また、デフォルト値の情報も提供されません。また、外部libに依存しない軽量関数も必要でした。

この関数は、デバッグの目的で非常に役立ちます。たとえば、呼び出された関数をそのパラメーター、デフォルトのパラメーター値および引数でログに記録します。

私は昨日これにしばらく時間をかけ、この問題を解決するために適切な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を使用して値を抽出することを複雑にします。

これが役に立ったかどうか教えてください!いくつかのフィードバックを聞きたいです!


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 2017年

この答えは役に立ちますが、質問には答えません。問題が解決したので投票しましたが、もっと適切な場所に移動する必要があると思います。おそらく関連する質問を検索しますか?
chharvey 2017年

1

このパッケージは、ASTを作成するためにリキャストを使用し、パラメーター名をそれらから収集します。これにより、パターンマッチング、デフォルト引数、矢印関数、およびその他のES6機能をサポートできます。

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


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({})


0

「引数」プロパティを使用して、関数に渡された引数値にアクセスできます。

    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 2009年

vikasdeは正しいです。arguments関数インスタンスのプロパティへのアクセスは非推奨です。developer.mozilla.org/en/Core_JavaScript_1.5_Reference/…を
Ionu Jun G. Stan

0

とても簡単です。

最初は非推奨arguments.callee—呼び出された関数への参照です。2番目に、関数への参照がある場合、それらのテキスト表現を簡単に取得できます。3番目に、コンストラクターとして関数を呼び出す場合は、yourObject.constructorを介してリンクを設定することもできます。注意:最初のソリューションは廃止されたので、それを使用できない場合は、アプリのアーキテクチャについても検討する必要があります。正確な変数名が必要ない場合は、関数の内部変数内で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(両方とも関数オブジェクト自体を指す)を使用した場合、これはさらに簡単です。
Raphael Schweikert

0

解決策が何であれ、奇妙に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

これが1つの方法です。

// 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 2017年

args.toSourceは関数ではありません(20行目)...しかし、次のように変更しても:console.log(args.toString()); ...取得します... [オブジェクトオブジェクト] ...実行した方がよい:console.log(JSON.stringify(args));
ジョージ2.0ホープ

2009年以降、状況は大きく変わりました。
Ates Goral 2017年

1
誰かがReactにここに現れた場合、素晴らしいこの関数はstrictモードでは機能しません。
個人

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.