फ़ंक्शन पैरामीटर नाम / मान को गतिशील रूप से कैसे प्राप्त करें?


300

क्या किसी फ़ंक्शन के फ़ंक्शन पैरामीटर को गतिशील रूप से प्राप्त करने का एक तरीका है?

मान लें कि मेरा फ़ंक्शन इस तरह दिखता है:

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

अब, मैं फ़ंक्शन के अंदर से किसी सरणी में पैरामीटर नाम और उनके मूल्यों की सूची कैसे प्राप्त करूंगा?


सभी को धन्यवाद। चारों ओर खोज करने के बाद, मैंने SO: stackoverflow.com/questions/914968/… पर समाधान पाया। यह परम नाम प्राप्त करने के लिए एक रेगेक्स का उपयोग करता है। इसका शायद सबसे अच्छा समाधान नहीं है, हालांकि यह मेरे लिए काम करता है।
vikasde

8
आगे बढ़ने दें और इसे उत्तर दिए गए मित्र के रूप में चिह्नित करें। कोई नया जवाब नहीं आ रहा है।
मैथ्यू ग्रेव्स

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

संपादित करें :

ईएस 6 के आविष्कार के साथ इस फ़ंक्शन को डिफ़ॉल्ट मापदंडों द्वारा ट्रिप किया जा सकता है। यहाँ एक त्वरित हैक है जो ज्यादातर मामलों में काम करना चाहिए:

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

मैं ज्यादातर मामलों का कहना है क्योंकि कुछ चीजें हैं जो इसे यात्रा करेंगे

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

संपादित करें : मैं यह भी नोट करता हूं कि vikasde एक सरणी में भी पैरामीटर मान चाहता है। यह पहले से ही स्थानीय चर नाम के तर्कों में प्रदान किया गया है।

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments से उद्धरण :

तर्क वस्तु एक ऐरे नहीं है। यह एक ऐरे के समान है, लेकिन इसमें लंबाई के अलावा कोई ऐरे गुण नहीं है। उदाहरण के लिए, यह पॉप विधि नहीं है। हालाँकि इसे एक वास्तविक ऐरे में परिवर्तित किया जा सकता है:

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
जब कोई तर्क न हो तो मैं खाली सरणी (नल के बजाय) वापस करने के लिए फ़ंक्शन को संशोधित करता हूं
बीटी

2
एक regex को संकलित करने की लागत है, इसलिए आप जटिल regex को एक से अधिक बार संकलित करने से बचना चाहते हैं। यही कारण है कि यह समारोह के बाहर किया जाता है
जैक एलन

2
सुधार: एक / s संशोधक के साथ regex को संशोधित करने जा रहा था जो perl को अनुमति देता है '।' न्यूलाइन से भी मिलान कर सकते हैं। यह * * * / के अंदर मल्टी-लाइन टिप्पणियों के लिए आवश्यक है। जावास्क्रिप्ट regex को चालू / संशोधित करने की अनुमति नहीं देता है। [/ S / s] का उपयोग करके मूल रेगेक्स, नईलाइन वर्णों से मेल खाता है। SOOO, कृपया पिछली टिप्पणी की अवहेलना करें।
tgoneil

1
@andes नोट आप अपने परीक्षणों में रेगेक्स संकलन सहित शामिल हैं। ये केवल एक बार किया जाना चाहिए। आपके परिणाम भिन्न हो सकते हैं यदि आप टेस्ट के बजाय रीगेक्स संकलन को सेटअप चरण में ले जाते हैं
जैक एलन

123

नीचे AngularJS से लिया गया कोड है जो अपनी निर्भरता इंजेक्शन तंत्र के लिए तकनीक का उपयोग करता है।

और यहाँ http://docs.angularjs.org/tutorial/step_05 से लिया गया विवरण है

कोणीय की निर्भरता इंजेक्टर आपके नियंत्रक को सेवाएं प्रदान करता है जब नियंत्रक का निर्माण किया जा रहा है। निर्भरता इंजेक्टर भी सेवा में हो सकता है किसी भी सकरात्मक निर्भरता बनाने का ख्याल रखता है (सेवाएं अक्सर अन्य सेवाओं पर निर्भर करती हैं)।

ध्यान दें कि तर्कों के नाम महत्वपूर्ण हैं, क्योंकि इंजेक्टर निर्भरता को देखने के लिए इनका उपयोग करता है।

/**
 * @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
राक्षसों के खून और शैतान के अंडे के साथ @apaidner, जाहिरा तौर पर। Regex ?! अगर जेएस में एक तरह से बनाया गया था, तो यह अच्छा नहीं होगा।
आदित्य सांसद

6
@apaidnerd, तो सच है! जरा सोचा- यह कैसे लागू किया जाता है? वास्तव में मैंने functionName.toString () का उपयोग करने के बारे में सोचा था, लेकिन मुझे कुछ और सुरुचिपूर्ण (और शायद तेज़) के लिए आशा थी
sasha.sochka

2
@ sasha.sochka, यहाँ ठीक उसी बात को सोचकर आया था, जिसे महसूस करने के बाद जावास्क्रिप्ट के साथ पैरामीटर नाम प्राप्त करने के लिए कोई रास्ता नहीं बनाया गया था
हार्ट सिम्हा

14
लोग समय बचाने के लिए, तो आप इस समारोह कोणीय से के माध्यम से प्राप्त कर सकते हैं annotate = angular.injector.$$annotate
निक

3
मैं सचमुच इस विषय के लिए इंटरनेट पर खोज कर रहा था क्योंकि मैं उत्सुक था कि एंगुलर ने यह कैसे किया ... अब मुझे पता है और मुझे भी पता है!
स्टीवन हंट

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
आज़माइए

4
आप शायद बदल दिया जाना चाहिए func + ''के साथ Function.toString.call(func)मामले समारोह एक कस्टम .toString () कार्यान्वयन है जब के खिलाफ की रक्षा करने के लिए।
पॉल गो

1
वसा तीर =>.split(/\)[\{=]/, 1)[0]
मैट

1
यह विनाशकारी वस्तुओं को नष्ट कर देता है (जैसे ({ a, b, c })) विनाश के अंदर सभी मापदंडों में। नष्ट हुई वस्तुओं को अक्षुण्ण बनाए रखने के लिए, अंतिम .splitको बदलें : .split(/,(?![^{]*})/g)
माइकल ऑडर

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 एक बात जो मैंने देखी है कि ऐरो फ़ंक्शंस के लिए कल्पना कहती है कि उन्हें 'फ़ंक्शंस' नहीं माना जाना चाहिए। इसका मतलब यह है कि यह सरणी फ़ंक्शन से मेल खाने के लिए उपयुक्त नहीं होगा। 'यह' वैसे ही सेट नहीं है, और उन्हें फ़ंक्शन के रूप में भी लागू नहीं किया जाना चाहिए। यह कुछ ऐसा था जिसे मैंने कठिन तरीके से सीखा। ($ myService) => $ myService.doSomething () अच्छा लग रहा है, लेकिन यह Arra Functions का दुरुपयोग है।
एंड्रयू टी फिननेल

20

यहाँ पर बहुत सारे उत्तर रेगीज़ का उपयोग करते हैं, यह ठीक है लेकिन यह भाषा के नए परिवर्धन को बहुत अच्छी तरह से नहीं संभालता है (जैसे तीर के कार्य और कक्षाएं)। यह भी ध्यान दें कि यदि आप इनमें से किसी भी प्रकार का उपयोग करते हैं तो यह going जाने वाला है। इसका जो भी नाम होगा उसका इस्तेमाल होगा। कोणीय इस के चारों ओर हो जाता है जिससे आप डीआर कंटेनर के साथ पंजीकरण करते समय तर्कों के एक क्रमबद्ध सरणी में पारित कर सकते हैं जो तर्कों के क्रम से मेल खाता है। तो समाधान के साथ:

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

यह वहाँ से बाहर सबसे उन्नत रिज़ॉल्वर नहीं है, लेकिन अगर आप सरल डि के लिए आर्ग्स पार्सर का उपयोग करना चाहते हैं तो आप इसे संभालने के लिए प्रॉक्सी का उपयोग कैसे कर सकते हैं, इसका एक विचार देता है। इस दृष्टिकोण में एक मामूली चेतावनी है। हमें सामान्य पारमों के बजाय विनाशकारी असाइनमेंट का उपयोग करने की आवश्यकता है। जब हम इंजेक्टर प्रॉक्सी में पास होते हैं तो विनाशकारी वस्तु पर गेट्टर को कॉल करने के समान होता है।

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

इसने पूरे आवेदन को तार-तार कर दिया। सबसे अच्छा बिट यह है कि ऐप का परीक्षण करना आसान है (आप प्रत्येक कक्षा को तुरंत लिख सकते हैं और मोक्स / स्टब्स / आदि में पास कर सकते हैं)। इसके अलावा, यदि आपको कार्यान्वयन को पूरा करने की आवश्यकता है, तो आप एक ही स्थान से ऐसा कर सकते हैं। यह सब जेएस प्रॉक्सी वस्तुओं की वजह से संभव है।

नोट: ऐसे बहुत से काम हैं, जिन्हें उत्पादन के उपयोग के लिए तैयार होने से पहले करने की आवश्यकता होगी, लेकिन यह एक विचार देता है कि यह कैसा दिखेगा।

यह उत्तर में थोड़ा देर से है लेकिन यह उन लोगों की मदद कर सकता है जो एक ही चीज के बारे में सोच रहे हैं। 👍


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
मुझे लगता है कि इस तरह का मामला आम तौर पर होता है: डिबगिंग / लॉगिंग, कुछ प्रकार के डेकोरेटर जो फंकी सामान (तकनीकी शब्द something) करते हैं, या तर्क नामों के आधार पर स्वचालित रूप से इंजेक्ट करने के लिए आपके ऐप्स के लिए एक निर्भरता इंजेक्शन फ्रेमवर्क बनाते हैं (यह इस तरह से कोणीय है काम करता है)। एक और वास्तव में दिलचस्प उपयोग मामला प्रॉमिस-नोड में है (यह एक पुस्तकालय है जो एक फ़ंक्शन लेता है जो सामान्य रूप से कॉलबैक लेता है और फिर इसे एक प्रॉमिस में परिवर्तित करता है)। वे कॉलबैक (जैसे कि cb / कॉलबैक / etc) के लिए सामान्य नामों को खोजने के लिए इसका उपयोग करते हैं और फिर वे जांच सकते हैं कि फ़ंक्शन रैप करने से पहले यह async या सिंक है या नहीं।
जेम्स ड्रू

इस फाइल को उनके पार्सर के लिए देखें । यह थोड़ा भोला है, लेकिन यह अधिकांश मामलों को संभालता है।
जेम्स ड्रू

दिलचस्प है, मुझे आश्चर्य है कि इस तरह के पुस्तकालय पर कोई ध्यान गया। खैर, समस्याग्रस्त स्थितियों के बारे में कई खुले मुद्दे हैं जैसे मैंने वर्णित किया था। जैसा कि मैंने कहा, अगर यह डिबगिंग के उद्देश्यों के लिए है, तो यह ठीक है, लेकिन उत्पादन वातावरण में कार्यों के स्ट्रिंग रूपांतरण पर निर्भर करना बहुत जोखिम भरा है।
डोमिनोज

एक "मज़ेदार" कटाक्ष पर, आप सभी कार्यों को ऐसे कर सकते हैं मानो वे इस तरह से चलकर अनाम बनाया गया हो:Function.prototype.toString = function () { return 'function () { [native code] }'; };
डोमिनोज़

1
सहमत, इसे इस तरह से संभालना थोड़ा गड़बड़ है। सबसे अच्छा और सबसे सीधा उपयोग निर्भरता इंजेक्शन है। उस स्थिति में आप कोड के मालिक हैं और नामकरण और कोड के अन्य क्षेत्रों को संभाल सकते हैं। मुझे लगता है कि यह वह जगह है जहां यह सबसे अधिक उपयोग होगा। वर्तमान में esprima (regex के बजाय) और ES6 Proxy( निर्माता जाल और लागू जाल ) और Reflectionमेरे कुछ मॉड्यूल के लिए DI को संभालने के लिए उपयोग कर रहे हैं। यह काफी ठोस है।
जेम्स ड्रू

10

चूंकि जावास्क्रिप्ट एक स्क्रिप्टिंग भाषा है, मुझे लगता है कि इसके आत्मनिरीक्षण को फ़ंक्शन पैरामीटर नाम प्राप्त करने का समर्थन करना चाहिए। उस कार्यक्षमता पर दंड देना पहले सिद्धांतों का उल्लंघन है, इसलिए मैंने आगे इस मुद्दे का पता लगाने का फैसला किया।

इसने मुझे इस प्रश्न के लिए प्रेरित किया लेकिन कोई अंतर्निहित समाधान नहीं। जो मुझे इस उत्तर की ओर ले गया, जो बताता है कि 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)कि डिफ़ॉल्ट मान के रूप में उपयोग किया जा सकता है। तो गैफ़र का जवाब या जेम्स ड्रू का जवाब अब तक का सबसे अच्छा तरीका है।

मैंने बाबुल और एस्प्रिमा पार्सर्स की कोशिश की, लेकिन दुर्भाग्य से वे स्टैंडअलोन अनाम कार्यों को पार्स नहीं कर सकते हैं, जैसा कि माटेउसज़ चेरिटोनियुक के जवाब में बताया गया है । मैंने कोष्ठक में कोड को घेरकर एक और समाधान निकाला है, ताकि तर्क को बदलना न हो:

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

नए अंक //(एकल-पंक्ति टिप्पणियों) के साथ समस्याओं को रोकते हैं ।

⭐ यदि एक पार्सर उपलब्ध नहीं है, तो अगला-सबसे अच्छा विकल्प एंगुलर.जेएस की निर्भरता इंजेक्टर रेगुलर एक्सप्रेशंस की तरह आजमाई हुई-सच्ची तकनीक का उपयोग करना है। मैं का एक कार्यात्मक संस्करण संयुक्त Lambder के जवाब के साथ humbletim के जवाब और एक वैकल्पिक जोड़ा ARROWनियंत्रित करने के लिए कि क्या ES6 वसा तीर कार्यों नियमित अभिव्यक्ति की अनुमति होती है के लिए बूलियन।


यहाँ दो समाधान हैं जिन्हें मैंने एक साथ रखा है। ध्यान दें कि इनका यह पता लगाने के लिए कोई तर्क नहीं है कि किसी फ़ंक्शन के पास सिंटैक्स है या नहीं, वे केवल तर्क निकालते हैं। यह आमतौर पर ठीक है क्योंकि हम आमतौर पर पार्स किए गए कार्यों को पास करते हैं getArguments()ताकि उनका सिंटैक्स पहले से ही मान्य हो।

मैं इन समाधानों को सर्वश्रेष्ठ करने की कोशिश करूंगा जो कि मैं कर सकता हूं, लेकिन जावास्क्रिप्ट अनुरक्षकों के प्रयास के बिना, यह एक खुली समस्या रहेगी।

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 में पहली शर्त तीर और पारंपरिक कार्यों दोनों के लिए काम करेगी, इसलिए आपको दूसरे भाग की आवश्यकता नहीं है और इसलिए आप ARROW पर निर्भरता से दूर कर सकते हैं।
pwilcox

यह भी खूब रही! मैं इस तरह के समाधान की तलाश कर रहा था जो ईएस 6 सिंटैक्स को कवर करने के लिए एक पार्सर का उपयोग करता है। मैं इसका उपयोग करने की योजना बना रहा हूं, ताकि एक जेस्ट "इम्प्लीमेंट्स इंटरफ़ेस" माचिस बनाई जा सके क्योंकि फंक्शन का उपयोग करके।लिफ्टिंग में डिफॉल्ट मापदंडों के साथ सीमाएं हैं, और मैं बाकी मापदंडों को सुनिश्चित करने में सक्षम होना चाहता था।
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"

उस कोड को एक फ़ंक्शन के सेटअप के साथ भी काम किया जाएगा जो कैमिलो मार्टिन ने दिया था:

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

जैक एलन के जवाब पर बबर्सन की टिप्पणी के साथ :

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

__

व्याख्या

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

यह के साथ एक नियमित अभिव्यक्ति बनाता है new RegExp('(?:'+Function.name+'\\s*|^)\\s*\\((.*?)\\)')। मुझे उपयोग करना है new RegExpक्योंकि मैं Function.nameRegExp में एक चर ( फ़ंक्शन का नाम लक्षित किया जा रहा है) को इंजेक्ट कर रहा हूं ।

उदाहरण यदि फ़ंक्शन का नाम "फू" ( function foo()) है, तो RegExp होगा /foo\s*\((.*?)\)/

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

फिर यह पूरे फ़ंक्शन को एक स्ट्रिंग में परिवर्तित करता है, और सभी नए सिरे को हटा देता है। समारोह की स्थापना के साथ मदद करता है हटाने के लिए कैमिलो मार्टिन ने दिया।

.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
एटी

@ मैंने आपके मुद्दे के समर्थन को ठीक करने के लिए उत्तर दिया है
Jaketr00

धन्यवाद ... लेकिन ध्यान रखें कि कोष्ठक की अब आवश्यकता नहीं है, इसलिए आप ऐसे काम कर सकते हैं getParameters(a => b => c => d => a*b*c*d), जो आपके कोड के साथ अभी भी देता है TypeError: Cannot read property '1' of null... जबकि यह एक stackoverflow.com/a/29123804
AT

कार्य नहीं करता है जब फ़ंक्शन में डिफ़ॉल्ट मान (भूमिका, नाम = "बॉब") निकाले गए पैरामीटर का नाम = "बॉब" अपेक्षित "नाम" के बजाय है
दिमित्री


7

आप पैरामीटर सूची के अंदर टिप्पणियों, व्हाट्सएप और अन्य चीजों के साथ कई मुद्दों से बचने के लिए "एस्प्रिमा" पार्सर का भी उपयोग कर सकते हैं।

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

2
यह प्रतिक्रिया मूल प्रश्न का उत्तर नहीं है जिसे सटीक रूप से परिभाषित किया गया था। यह पूरी तरह से अलग समस्या के लिए एक समाधान दिखाता है। मूल प्रश्न तकनीक को संदर्भित करता है AngularJS अपनी निर्भरता इंजेक्शन का उपयोग करता है। तर्क नाम सार्थक हैं क्योंकि वे उस मॉड्यूल की निर्भरता के अनुरूप हैं जो DI स्वचालित रूप से प्रदान करता है।
लैम्ब्डर

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

ऐसा करने का उचित तरीका है जेएस पार्सर का उपयोग करना। यहां एकोर्न का उपयोग करके एक उदाहरण दिया गया है ।

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


मूल्यों के बारे में क्या?
ओटजाप ऑक्ट

2

मैं नहीं जानता कि मापदंडों की एक सूची कैसे प्राप्त की जाए लेकिन आप यह अपेक्षा कर सकते हैं कि यह कितने की अपेक्षा रखता है।

alert(doSomething.length);

function gotcha (a, b = false, c) {}; alert(gotcha.length)
बालूपटन

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
धन्यवाद, इस धागे में तुम्हारा ही है जो मेरे लिए काम करता है।
एटी

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: 'string'} जैसे कुछ का आउटपुट? इससे चीजें साफ हो सकती हैं (function fn (arg, argg, arrrrgggg) { console.log('#fn:', fn.arguments, Object.keys(fn.arguments)); }); fn('Huh...?', 'Wha...?', 'Magic...?');:। समारोह तर्क एक 'Array' जैसी वस्तु है, जिसमें अनगिनत संकेत होते हैं। मुझे नहीं लगता कि कोई हैश-मैपिंग संभव है, लेकिन आप बस एक ऑब्जेक्ट-शाब्दिक पास कर सकते हैं जिसमें अच्छा अभ्यास है यदि आपके पास वैसे भी 4 से अधिक पैरा हैं।
कोडी

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. अंतिम चरण 1 सरणी में 2 सरणियों को एक साथ जोड़ना है जिसमें सभी मापदंडों का नाम और मूल्य है।

कोड इस तरह होगा

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

यह उदाहरण सिर्फ आपके लिए मापदंडों का नाम लेने के लिए है।
मायजियो

यह उत्तर उपयोगी है, लेकिन यह प्रश्न का उत्तर नहीं देता है। मैंने मतदान किया क्योंकि इससे मेरी समस्या हल हो गई, लेकिन मुझे लगता है कि इसे कहीं और उचित स्थान पर ले जाना चाहिए। शायद संबंधित प्रश्नों की खोज करें?
छरवे

1

यह पैकेज एएसटी बनाने के लिए रीकास्ट का उपयोग करता है और फिर पैरामीटर नाम उनके से एकत्र किए जाते हैं, इससे यह पैटर्न मिलान, डिफ़ॉल्ट तर्क, तीर फ़ंक्शन और अन्य ES6 सुविधाओं का समर्थन करने की अनुमति देता है।

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


1

मैंने AngularJS से लिया गया संस्करण संशोधित किया है जो Angular के बिना काम करने के लिए एक निर्भरता इंजेक्शन तंत्र को लागू करता है। मैंने STRIP_COMMENTSregex को काम करने के लिए भी अद्यतन किया है 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 सही है। argumentsएक फ़ंक्शन इंस्टेंस की संपत्ति तक पहुँच को पदावनत किया जाता है। डेवलपर
। pmillailla.org/en/Core_JavaScript_1.5_Reference/…

0

यह बहुत आसान है।

सबसे पहले एक पदावनत arguments.calleeकिया जाता है - जिसे फ़ंक्शन कहा जाता है। दूसरे पर यदि आपके पास अपने फ़ंक्शन का संदर्भ है, तो आप आसानी से उनके पाठ्य का प्रतिनिधित्व प्राप्त कर सकते हैं। तीसरे पर यदि आप अपने फ़ंक्शन को कंस्ट्रक्टर के रूप में बुला रहे हैं, तो आपके पास आपकेObject.constructor के माध्यम से एक लिंक भी हो सकता है। एनबी: पहला समाधान तब खाली हो जाता है जब आप इसका उपयोग नहीं कर सकते हैं तो आपको अपने ऐप आर्किटेक्चर के बारे में भी सोचना चाहिए। यदि आपको सटीक चर नामों की आवश्यकता नहीं है, तो argumentsबिना किसी जादू के फ़ंक्शन आंतरिक चर के अंदर उपयोग करें ।

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

उन सभी को कॉल करने के लिए जा रहा है जो स्ट्रिंग और री के साथ प्रतिस्थापित करता है ताकि हम एक सहायक बना सकें:

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

जेएस के साथ का आनंद लें!

UPD: जैक एलन को वास्तव में थोड़ा बेहतर समाधान प्रदान किया गया था। जीजे जैक!


यह और भी सरल हो सकता है यदि आप SomeFuncNameइसके बजाय arguments.callee(दोनों फंक्शन ऑब्जेक्ट को इंगित करते हैं) का उपयोग करते हैं।
राफेल श्विकर्ट

0

जो भी समाधान है, यह अजीब कार्यों पर नहीं टूटना चाहिए, जिसका रंग toString()केवल अजीब लगता है:

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

कंसोल से स्क्रीनशॉट

इसके अलावा, जटिल नियमित अभिव्यक्तियों का उपयोग क्यों करें? इसे इस तरह किया जा सकता है:

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

यह हर फ़ंक्शन के साथ हर जगह काम करता है, और एकमात्र regex व्हाट्सएप हटाने है जो .splitचाल के कारण पूरे स्ट्रिंग को संसाधित नहीं करता है ।


0

ठीक है तो पर्याप्त उत्तर के साथ एक पुराना सवाल। यहाँ मेरा प्रस्ताव है कि व्हाट्सएप्प को छीनने के मासिक धर्म के अलावा रेगेक्स का उपयोग नहीं किया जाता है। (मुझे ध्यान देना चाहिए कि "स्ट्रिप्स_काम" फ़ंक्शन वास्तव में उन्हें बाहर निकालता है, बजाय उन्हें शारीरिक रूप से हटाने के। इसलिए क्योंकि मैं इसे कहीं और उपयोग करता हूं और विभिन्न कारणों से अक्षुण्ण रहने के लिए मूल गैर टिप्पणी टोकन के स्थानों की आवश्यकता होती है)

यह कोड का एक काफी लंबा ब्लॉक है क्योंकि इस चिपकाने में एक मिनी टेस्ट फ्रेमवर्क शामिल है।

    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
जबकि लूप परिणाम प्रदान किए गए कोड के साथ एक अनंत लूप में है (20171121at1047EDT के रूप में)
जॉर्ज 2.0 होप

@ George2.0Hope इस ओर इशारा करने के लिए धन्यवाद। मैं जवाब अपडेट कर दूंगा।
एतेस गोराल

args.toSource एक फ़ंक्शन (पंक्ति 20) नहीं है ... लेकिन भले ही आप इसे बदल दें: कंसोल.log (args.toString ()); ... आपको मिलता है ... [वस्तु वस्तु] ... बेहतर है अगर आप करते हैं: कंसोल.लॉग (JSON.stringify (args));
जॉर्ज 2.0 होप

2009 से हालात काफी बदल गए हैं!
एटलेस गोरल

1
अगर कोई भी रिएक्ट के लिए यहां दिखाता है, तो यह फ़ंक्शन, जो भयानक है, सख्त मोड में काम नहीं करेगा।
व्यक्तिगत

0

नोट: यदि आप शीर्ष समाधान के साथ विनाशकारी ES6 पैरामीटर का उपयोग करना चाहते हैं तो निम्न पंक्ति जोड़ें।

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

-1

फ़ंक्शन पैरामीटर स्ट्रिंग मान छवि JSON से गतिशील रूप से । चूंकि item.product_image2 एक URL स्ट्रिंग है, इसलिए जब आप पैरामीटर के अंदर changeImage कहते हैं, तो आपको इसे उद्धरण में रखने की आवश्यकता होती है।

माई फंक्शन ओनक्लिक

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>
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.