Fonksiyon parametre adları / değerleri dinamik olarak nasıl elde edilir?


301

Bir işlevin işlev parametre adlarını dinamik olarak almanın bir yolu var mı?

Diyelim ki işlevim şöyle görünüyor:

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

Şimdi, parametre adlarının ve değerlerinin bir listesini işlevin içinden bir diziye nasıl alabilirim?


Herkese teşekkürler. Etrafta aradıktan sonra, çözümü SO üzerinde buldum: stackoverflow.com/questions/914968/… Param adını almak için bir normal ifade kullanıyor. Muhtemelen en iyi çözüm değil, ancak benim için çalışıyor.
vikasde

8
devam edelim ve bunu cevaplanmış arkadaş olarak işaretleyelim. yeni cevap gelmiyor.
Matthew Graves

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

Yanıtlar:


323

Aşağıdaki işlev, iletilen herhangi bir işlevin parametre adlarından oluşan bir dizi döndürür.

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

Örnek kullanım:

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

Düzenle :

ES6'nın icadı ile bu fonksiyon varsayılan parametrelerle tetiklenebilir. İşte çoğu durumda çalışması gereken hızlı bir kesmek:

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

Çoğu vakayı söylüyorum çünkü onu harekete geçirecek bazı şeyler var

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

Düzenleme : Ayrıca vikasde bir dizideki parametre değerlerini istediğini unutmayın. Bu zaten arguments adlı yerel bir değişkente sağlanmıştır.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments adresinden alıntı :

Arguments nesnesi bir Array değildir. Bir Diziye benzer, ancak uzunluk dışında herhangi bir Dizi özelliği yoktur. Örneğin, pop yöntemi yoktur. Ancak, gerçek bir Diziye dönüştürülebilir:

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

Dizi jenerikleri varsa, bunun yerine aşağıdakiler kullanılabilir:

var args = Array.slice(arguments);

12
Bu çözümün yorum ve boşluklardan dolayı başarısız olabileceğini unutmayın - örneğin: var fn = function(a /* fooled you)*/,b){};sonuçlanacak ["a", "/*", "fooled", "you"]
bubersson

1
Herhangi bir argüman olmadığında boş bir dizi (null yerine) döndürmek için işlevi değiştirdim
BT

2
Bir normal ifadeyi derlemenin bir maliyeti vardır, bu nedenle karmaşık normal ifadeleri bir kereden fazla derlemekten kaçınmak istersiniz. Bu yüzden fonksiyonun dışında yapılır
Jack Allan

2
DÜZELTME: Normal ifadeye izin veren bir / s değiştiricisi ile normal ifadeyi değiştirecekti. yeni satırla da eşleşebilir. Bu, / * * / içindeki çok satırlı yorumlar için gereklidir. Javascript normal ifadesi / s değiştiricisine izin vermez. [/ S / S] kullanılan orijinal normal ifade yeni satır karakterleriyle eşleşir. SOOO, lütfen önceki yorumu dikkate almayın.
tgoneil

1
@andes Testlerinize normal ifade derlemesini eklediğinizi unutmayın. Bunlar sadece bir kez yapılmalıdır. Normal ifade derlemesini test yerine kurulum adımına taşırsanız sonuçlarınız farklı olabilir
Jack Allan

123

Aşağıda bağımlılık enjeksiyon mekanizması için tekniği kullanan AngularJS'den alınan kod bulunmaktadır.

Ve işte bunun http://docs.angularjs.org/tutorial/step_05 adresinden alınmış bir açıklaması

Angular'ın bağımlılık enjektörü, kontrolör oluşturulurken kontrolörünüze servis sağlar. Bağımlılık enjektörü, hizmetin sahip olabileceği herhangi bir geçişli bağımlılık yaratmaya da özen gösterir (hizmetler genellikle diğer hizmetlere bağlıdır).

Enjektör bağımlılıkları aramak için bunları kullandığından argümanların adlarının önemli olduğunu unutmayın.

/**
 * @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
Görünüşe göre şeytanların kanı ve şeytanın doğuşu ile @apaidnerd. Regex ?! JS yerleşik bir yol olsaydı, serin olurdu, olmaz.
Aditya MP

6
@apaidnerd, çok doğru! Sadece düşündüm- bu nasıl cehennemde? Aslında functionName.toString () kullanmayı düşündünüz ama daha şık (ve belki de daha hızlı) bir şey için ümit
sasha.sochka

2
@ sasha.sochka, javascript ile parametre adları almak için hiçbir yerleşik olmadığını fark ettikten sonra, aynı şeyi merak ederek buraya geldi
Hart Simha

14
İnsanları zamandan kazanmak için, bu fonksiyonu açısal olarak alabilirsiniz annotate = angular.injector.$$annotate
Nick

3
Kelimenin tam anlamıyla internette bu konuyu aramaya gittim çünkü Angular'ın bunu nasıl yaptığını merak ettim ... şimdi biliyorum ve çok fazla şey biliyorum!
Steven Hunt

40

Yukarıda belirtilen tüm uç durumları kompakt bir şekilde ele almaya çalışan güncel bir çözüm:

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

Kısaltılmış test çıktısı (tam test senaryoları aşağıda eklenmiştir):

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


Tek satırlık yorumlar mevcut olduğunda bu durum kesilir. return (func+'') .replace(/[/][/].*$/mg,'') // strip single-line comments (line-ending sensitive, so goes first) .replace(/\s+/g,'') // remove whitespace
Şunu

4
Fonksiyonun özel bir .toString () uygulaması olduğunda büyük / küçük harfe karşı savunmak için func + ''ile değiştirmelisiniz Function.toString.call(func).
Paul Go

1
şişman oklar =>.split(/\)[\{=]/, 1)[0]
Mat

1
bu, yıkılmış nesneleri (gibi ({ a, b, c })) yıkım içindeki tüm parametrelere böler . yıkılan nesneleri olduğu gibi .splitbırakmak için sonuncuyu şu şekilde değiştirin : .split(/,(?![^{]*})/g)
Michael Auderer

1
Bu, "//" veya "/ *" içeren varsayılan dize değerleri olduğunda da çalışmaz
skerit

23

Boşluklara ve yorumlara daha az hataya neden olan çözüm şöyle olacaktır:

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

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

["hi", "you"]

1
@AlexMills Fark ettiğim bir şey, Ok Fonksiyonları için Spec'in 'fonksiyon' olarak ele alınmaması gerektiğini söylüyor. Yani bunun dizi işlevleriyle eşleşmesi uygun olmaz. 'This' aynı şekilde ayarlanmamıştır ve işlev olarak da çağrılmamalıdır. Zor yoldan öğrendiğim bir şeydi. ($ myService) => $ myService.doSomething () harika görünüyor, ancak Array İşlevlerinin kötüye kullanılması.
Andrew T Finnell

20

Buradaki cevapların çoğu regexes kullanıyor, bu iyi ama dile yeni eklemeler (ok fonksiyonları ve sınıflar gibi) ile çok iyi başa çıkmıyor. Ayrıca, bu işlevlerden herhangi birini küçültülmüş kodda kullanırsanız, gitmesi gerektiğini unutmayın. Küçültülmüş ad ne olursa olsun kullanır. Açısal, DI kabına kaydederken, değişkenlerin sırasıyla eşleşen sıralı bir dizeler dizisi geçirmenize izin vererek bu sorunu çözer. Böylece çözüm:

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

Bu, orijinal ayrıştırma sorununu ve birkaç işlev türünü daha (örneğin ok işlevleri) ele alır. İşte neyi işleyebileceği ve işleyemediği hakkında bir fikir:

// 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 Proxy'leri için ne kullanmak istediğinize bağlı olarak yıkım en iyi seçenek olabilir. Örneğin, bağımlılık enjeksiyonu için (param adlarını kullanarak) kullanmak isterseniz, aşağıdaki gibi yapabilirsiniz:

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

Buradaki en gelişmiş çözümleyici değil, ancak basit DI için args ayrıştırıcısını kullanmak istiyorsanız, Proxy'yi nasıl kullanacağınız hakkında bir fikir verir. Bununla birlikte, bu yaklaşımda hafif bir uyarı vardır. Normal parametreler yerine yıkıcı ödevler kullanmalıyız. Enjektör proxy'sini geçtiğimizde yıkım, nesne üzerinde alıcıyı çağırmakla aynıdır.

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

Bu, aşağıdakileri çıkarır:

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

Tüm uygulama kadar kablolu. En iyi bit, uygulamanın test edilmesi kolay olmasıdır (sadece her sınıfı somutlaştırabilir ve alaycı / taslaklar / vb. Ayrıca, uygulamaları değiştirmeniz gerekiyorsa, bunu tek bir yerden yapabilirsiniz. Tüm bunlar JS Proxy nesneleri nedeniyle mümkündür.

Not: Üretimde kullanıma hazır hale gelmeden önce yapılması gereken çok iş vardır, ancak neye benzeyeceği hakkında bir fikir verir.

Cevabın biraz geç ama aynı şeyi düşünen başkalarına yardımcı olabilir. 👍


13

Bu eski bir soru olduğunu biliyorum, ama yeni başlayanlar sanki bu herhangi bir kod iyi bir uygulama sanki etrafında bu paspas. Çoğu zaman, bir işlevin parametre adlarını kullanmak için dize temsilini ayrıştırmak zorunda kalmanız, kodun mantığındaki bir hatayı gizler.

Bir işlevin parametreleri aslında argumentsilk bağımsız değişken, arguments[0]ikincisi arguments[1]vb. Adlı dizi benzeri bir nesnede saklanır . Parantez içinde parametre adları yazmak, steno sözdizimi olarak görülebilir. Bu:

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

...aynıdır:

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

    console.log("does something");
}

Değişkenlerin kendileri, bir nesnenin özellikleri olarak değil, işlevin kapsamında saklanır. Parametre adını kod üzerinden almanın bir yolu yoktur, çünkü bu sadece insan dilinde değişkeni temsil eden bir semboldür.

Ben her zaman, özellikle bu argumentsdizi benzeri nesne nedeniyle, bir fonksiyonun dize temsilini hata ayıklama amaçlı bir araç olarak kabul . İlk olarak argümanlara isim vermeniz gerekmez. Dizge haline getirilmiş bir işlevi ayrıştırmayı denerseniz, aslında alabileceği ekstra adsız parametrelerden bahsetmez.

İşte daha da kötü ve daha yaygın bir durum. Bir fonksiyonun 3 veya 4'ten fazla argümanı varsa, bunun yerine bir nesneyi iletmek mantıklı olabilir, bu da çalışmak daha kolaydır.

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

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

Bu durumda, işlevin kendisi aldığı nesneyi okuyabilir ve özelliklerini arayabilir ve hem adlarını hem de değerlerini alabilir, ancak işlevin dize temsilini ayrıştırmaya çalışmak parametreler için yalnızca "obj" verir, ki bu hiç de kullanışlı değil.


1
Bunun gibi bir şey için durum genellikle: hata ayıklama / günlüğe kaydetme, funky şeyler yapan bir tür dekoratör (teknik terim or) veya uygulamalarınızın argüman adına göre otomatik olarak enjekte edilmesi için bir bağımlılık enjeksiyon çerçevesi oluşturma (bu nasıl açısaldır) İşler). Gerçekten ilginç bir başka kullanım örneği promisify-node'dur (normalde geri arama alan ve daha sonra bir Promise dönüştüren bir işlevi alan bir kütüphanedir). Bunu geri aramalar için ortak adlar bulmak için kullanırlar (cb / geri arama / vb. Gibi) ve sonra sarmalamadan önce işlevin asenkron veya senkronize olup olmadığını kontrol edebilirler.
James Drew

Ayrıştırıcılar için bu dosyaya bakın . Biraz naif ama davaların çoğunu hallediyor.
James Drew

İlginç, böyle bir kütüphanenin dikkat çekmesine şaşırdım. Açıkladığım gibi sorunlu durumlar hakkında birçok açık konu var. Söylediğim gibi, eğer hata ayıklama amaçlıysa, iyi, ancak üretim ortamlarındaki işlevlerin dize dönüşümüne güvenmek çok riskli.
Domino

Bir "komik" sidenote üzerinde, tüm işlevleri anonim yerleşik gibi davranarak yapabilirsiniz:Function.prototype.toString = function () { return 'function () { [native code] }'; };
Domino

1
Anlaşıldı, böyle işlemek biraz dağınık. En iyi ve en açık kullanım bağımlılık enjeksiyonudur. Bu durumda, kodun sahibi siz olursunuz ve adlandırma ile diğer kod alanlarını işleyebilirsiniz. Bence en çok kullanacağı yer burası. Şu anda esprima (regex yerine) ve ES6 Proxy( yapıcı tuzak ve tuzak uygulamak ) kullanarak ve Reflectionbazı benim modülleri için DI işlemek için Im . Oldukça sağlam.
James Drew

10

JavaScript bir komut dosyası dili olduğundan, içgözleminin işlev parametre adları almayı desteklemesi gerektiğini hissediyorum. Bu işlevsellikte bahis yapmak ilk ilkelerin ihlalidir, bu yüzden sorunu daha ayrıntılı olarak araştırmaya karar verdim.

Bu beni bu soruya yönlendirdi, ancak yerleşik çözüm yok. Bu da beni sadece işlevin dışında kullanım dışı bırakıldığını açıklayan bu cevaba yönlendirdi , böylece artık kullanamayız veya şunu elde ederiz:argumentsmyFunction.arguments

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

Kollarımızı toplama ve işe başlama zamanı:

⭐ Fonksiyon parametreleri almak için ayrıştırıcı gerekir, çünkü gibi karmaşık ifadeler 4*(5/3)varsayılan değerler olarak kullanılabilir. Bu yüzden Gaafar'ın cevabı veya James Drew'ın cevabı şimdiye kadarki en iyi yaklaşımlar.

Babil ve esprima ayrıştırıcılarını denedim ama ne yazık ki Mateusz Charytoniuk'un cevabında belirtildiği gibi bağımsız anonim fonksiyonları ayrıştıramıyorlar . Mantığı değiştirmek için değil, parantez içinde kodu çevreleyen başka bir geçici çözüm anladım:

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

Yeni satırlar //(tek satırlık yorumlar) ile ilgili sorunları önler .

Bir ayrıştırıcı yoksa, en iyi seçenek Angular.js'nin bağımlılık enjektörü düzenli ifadeleri gibi denenmiş ve gerçek bir teknik kullanmaktır. Lambder'ın cevabının işlevsel bir versiyonunu humbletim'in cevabı ile birleştirdimARROW ve düzenli ifadeler tarafından ES6 yağ ok fonksiyonlarına izin verilip verilmediğini kontrol etmek için isteğe bağlı bir boole ekledim .


İşte bir araya getirdiğim iki çözüm. Bunların bir işlevin geçerli sözdizimi olup olmadığını algılayacak bir mantığı olmadığını, yalnızca bağımsız değişkenleri ayıkladıklarını unutmayın. Genelde ayrıştırılmış işlevleri geçirdiğimiz için getArguments()bu genellikle uygundur, bu nedenle sözdizimleri zaten geçerlidir.

Bu çözümleri elimden gelenin en iyisini yapmaya çalışacağım, ancak JavaScript sürdürücülerinin çabaları olmadan bu açık bir sorun olmaya devam edecek.

Node.js sürümü (StackOverflow Node.js'yi destekleyene kadar çalıştırılamaz):

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

Tam çalışma örneği:

https://repl.it/repls/SandybrownPhonyAngles

Tarayıcı sürümü (ilk karmaşık varsayılan değerde durduğunu unutmayın):

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

Tam çalışma örneği:

https://repl.it/repls/StupendousShowyOffices


Yanılmıyorsam, tarayıcı sürümünüzde, FUNC_ARGS'deki ilk koşul hem ok hem de geleneksel işlevler için çalışacaktır, bu nedenle ikinci kısma ihtiyacınız yoktur ve böylece OK bağımlılığını ortadan kaldırabilirsiniz.
pwilcox

Bu harika! ES6 sözdizimini kapsamak için bir ayrıştırıcı kullanan böyle bir çözüm arıyordum. Ben sadece işlev.length kullanarak varsayılan parametreleri ile sınırlamaları vardır ve ben dinlenme parametreleri iddia edebilmek istedim çünkü bir jest "uygular" eşleştirici oluşturmak için bunu kullanmayı planlıyorum.
cue8chalk

Varsayılan değerde parantez içeren beşinci test durumunun başarısız olduğunu belirtmek gerekir. Benim regex-fu düzeltmek için yeterince güçlü olsaydı, üzgünüm!
Dale Anderson

8

Cevapların çoğunu burada okudum ve tekli astarımı eklemek istiyorum.

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

veya

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

veya ECMA6'da bir astar işlevi için

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

__

Diyelim ki bir fonksiyonunuz var

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

Aşağıdaki kod geri dönecektir "abc,def,ghi,jkl"

Bu kod, Camilo Martin'in verdiği bir fonksiyonun kurulumuyla da çalışacaktır :

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

Ayrıca Bubersson'un Jack Allan'ın cevabı hakkındaki yorumu ile :

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

__

açıklama

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

Bu yaratır Düzenli İfade ile new RegExp('(?:'+Function.name+'\\s*|^)\\s*\\((.*?)\\)'). RegExp içine new RegExpbir değişken ( Function.name, hedeflenen işlevin adı) enjekte ediyorum çünkü kullanmak zorundayım .

Örnek İşlev adı "foo" ( function foo()) ise, RegExp olacaktır /foo\s*\((.*?)\)/.

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

Ardından, tüm işlevi bir dizeye dönüştürür ve tüm yeni satırları kaldırır. Yeni satırları kaldırmak, Camilo Martin'in verdiği işlev kurulumuna yardımcı olur .

.exec(...)[1]

Bu RegExp.prototype.execişlev. Temelde Düzenli Üs ( new RegExp()) ile String ( Function.toString()) ile eşleşir . Ardından, Düzenli Üste ( ) bulunan [1]ilk Yakalama Grubu döndürülür (.*?).

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

Bu, her açıklama içini kaldıracak /*ve */ve tüm boşlukları kaldırın.


Bu ayrıca artık normal işlevler yerine dönecek olan arrow ( =>) işlevlerini okumayı ve anlamayı da desteklemektedir . Orijinal normal ifade, karışıklığında bir hata atar, ancak şimdi açıklanır.f = (a, b) => void 0;Function.toString()(a, b) => void 0function f(a, b) { return void 0; }

Değişiklik new RegExp(Function.name+'\\s*\\((.*?)\\)')( /Function\s*\((.*?)\)/) ile new RegExp('(?:'+Function.name+'\\s*|^)\\((.*?)\\)')( /(?:Function\s*|^)\((.*?)\)/) arasındaydı


Virgülle ayrılmış bir Dize yerine tüm parametreleri Array'a yapmak istiyorsanız, sonunda ekleyin .split(',').


gayet iyi. bir satır, ok işlevlerini yönetir, harici bağımlılıkları yoktur ve en azından amaçladığım kullanım için yeterli kenardan fazla durumu kapsar. Eğer ele alacağınız işlev imzaları hakkında bazı makul varsayımlar yapabiliyorsanız, ihtiyacınız olan tek şey budur. Teşekkürler!
charlie roberts

Bu basit ok işleviyle çalışmaz f = (a, b) => void 0:; üzerinde getParameters(f)alıyorumTypeError: Cannot read property '1' of null
AT

@AT Sorununuz için desteği düzeltmek üzere cevabı güncelledim
Jaketr00

Teşekkürler… ancak parantezlerin artık gerekli olmadığını unutmayın, bu nedenle getParameters(a => b => c => d => a*b*c*d)kodunuzla hala bunu veren gibi şeyler yapabilirsiniz TypeError: Cannot read property '1' of null… oysa bu çalışıyor, stackoverflow.com/a/29123804
AT

İşlev varsayılan değerlere sahip olduğunda çalışmaz (rol, ad = "bob") Ayıklanan parametre, beklenen "ad" yerine ad = "bob" olur
Dmitri

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

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


7

"Esprima" ayrıştırıcısını, parametreler listesindeki yorumlar, boşluk ve diğer şeyler ile ilgili birçok sorunu önlemek için de kullanabilirsiniz.

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

Bu gibi kodlarla bile çalışır:

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

6

Bunu daha önce denedim, ama bunu yapmak için pratik bir yol bulamadım. Bunun yerine bir nesnenin içinden geçip onun üzerinden geçtim.

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

Tek bir nesne yerine standart parametrelerle çağrılan önceden tanımlanmış birçok işleve sahibim. Her şeyi değiştirmek çok zaman alacaktır.
Vikasde

2
Bu yanıt, kesin olarak tanımlanan orijinal sorunun cevabı değildir. Tamamen farklı bir problem için bir çözüm gösterir. Asıl soru AngularJS'nin bağımlılık enjeksiyonunda kullandığı tekniği ifade eder. Bağımsız değişken adları, DI'nin otomatik olarak sağladığı modülün bağımlılıklarına karşılık geldiğinden anlamlıdır.
Lambder

4

Bu çözümün sizin sorununuza uygun olup olmadığını bilmiyorum, ancak bunu kullanan kodu değiştirmek zorunda kalmadan istediğiniz işlevi yeniden tanımlamanızı sağlar. İşlev çağrıları "adlandırılmış parametreler" (tek bir karma parametresi) kullanabilirken, mevcut çağrılar konumlandırılmış parametreler kullanır.

Zaten mevcut işlev tanımlarını değiştireceğinizi düşündüm, neden sadece istediğinizi yapan bir fabrika işlevine sahip değilsiniz:

<!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>

Umarım yardımcı olur.


4

Bunu yapmanın uygun yolu bir JS ayrıştırıcı kullanmaktır. İşte meşe palamudu kullanan bir örnek .

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

Buradaki kod, işlevin üç (resmi) parametresinin adlarını bulur f. Bunu besleyerek fyapar acorn.parse().


değerler ne olacak?
eran otzap

2

Parametrelerin bir listesini nasıl alacağımı bilmiyorum ama kaç tane beklediğini almak için bunu yapabilirsiniz.

alert(doSomething.length);

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

2

@ Jack-allan'dan cevap alarak ES6 varsayılan özelliklerine izin vermek için işlevi biraz değiştirdim:

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

hala geri dönmek [ '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
Teşekkürler, bu iş parçacığında benim için çalışan tek kişi sizindir.
AT

1

Nasıl tipik olarak yaparım:

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

Bağımsız değişkenleri aşağıdaki gibi işlev adıyla da ref.

name.arguments;

Bu yardımcı olur umarım!


4
Fonksiyon parametrelerinin isimleri nerede?
Andrej

Ah ... Yani karma formda mı istiyorsun? Sanki: var args = name.arguments; console.log('I WANNa SEE', args);"{arg1: {...}, arg2: 'string'}" gibi bir şey çıktılar mı? Bu şeyleri açığa olabilir: (function fn (arg, argg, arrrrgggg) { console.log('#fn:', fn.arguments, Object.keys(fn.arguments)); }); fn('Huh...?', 'Wha...?', 'Magic...?');. İşlev Bağımsız Değişkenleri, Numaralandırılabilir indekslere sahip 'Dizi' benzeri bir nesnedir. Bir karma eşlemenin mümkün olduğunu düşünmüyorum, ancak yine de 4'ten fazla parametreniz varsa, iyi bir uygulama olan bir Nesne hazır bilgisini geçebilirsiniz.
Cody

1
//See this:


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

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

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

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

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

1

Bunun cevabı 3 adım gerektirir:

  1. İşleve geçirilen gerçek parametrelerin değerlerini almak için (diyelim argValues). Bu, mevcut olacağı için doğrudan ileriarguments fonksiyonun içinde .
  2. Parametre adlarını işlev imzasından almak için (diyelim ki argNames ). Bu kadar kolay değildir ve işlevin ayrıştırılmasını gerektirir. Karmaşık regex'i kendiniz yapmak ve kenar durumlar (varsayılan parametreler, yorumlar, ...) hakkında endişelenmek yerine, işlevi parametre adlarını alabileceğiniz soyut bir sözdizimi ağacına ayrıştıracak babylon gibi bir kütüphane kullanabilirsiniz.
  3. Son adım, 2 diziyi, tüm parametrelerin adı ve değerine sahip 1 diziye birleştirmektir.

Kod şöyle olacak

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)

ve kaydedilen nesne

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

Ve işte çalışan bir örnek 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

Vay be çok cevap zaten .. Eminim bu gömülü. Buna rağmen bunun bazıları için yararlı olabileceğini düşündüm.

ES6'da varsayılan değerlerle iyi çalışmadığı için seçilen cevaplardan tam olarak memnun kalmadım. Ayrıca, varsayılan değer bilgilerini de sağlamaz. Ayrıca harici bir lib'e bağlı olmayan hafif bir fonksiyon istedim.

Bu işlev hata ayıklama amaçları için çok kullanışlıdır, örneğin: işlevler parametrelerini, varsayılan parametre değerlerini ve bağımsız değişkenlerini kullanarak günlüğe kaydetme.

Ben bu sorunu çözmek için doğru RegExp çatlamak, dün bu biraz zaman geçirdim ve ben geldi budur. Çok iyi çalışıyor ve sonuçtan çok memnunum:

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

Bazı parametre adlarının kaybolduğunu söyleyebileceğiniz gibi, Babel transpiler bunları işlevden kaldırır. Bunu en son NodeJS'de çalıştırırsanız beklendiği gibi çalışır (Yorumlanan sonuçlar NodeJS'den alınmıştır).

Yorumda belirtildiği gibi başka bir not, varsayılan değer olarak satır içi ok işlevleriyle çalışmadığıdır. Bu sadece bir RegExp kullanarak değerleri ayıklamak karmaşık hale getirir.

Bunun sizin için yararlı olup olmadığını lütfen bize bildirin! Bazı geri bildirimler duymak isterim!


1

Aşağıda size kısa bir örnek vereceğim:

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

Bu örnek sadece parametrelerin adını sizin için almaktır.
myzhou

Bu cevap faydalıdır, ancak soruyu cevaplamamaktadır. Oy verdim çünkü sorunumu çözdü, ama bence daha uygun bir yere taşınmalı. Belki ilgili soruları mı arıyorsunuz?
chharvey

1

Bu paket, bir AST oluşturmak için yeniden düzen kullanır ve daha sonra parametre adları bunlardan toplanır, bu da desen eşleşmesini, varsayılan bağımsız değişkenleri, ok işlevlerini ve diğer ES6 özelliklerini desteklemesini sağlar.

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


1

AngularJS'den alınan ve Angular olmadan çalışmak için bir bağımlılık enjeksiyon mekanizması uygulayan sürümü değiştirdim . Ben de STRIP_COMMENTSregex ile çalışmak için güncelledim ECMA6, bu yüzden imza varsayılan değerleri gibi şeyler destekler.

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

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

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

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


0

"Arguments" özelliğini kullanarak bir işleve iletilen argüman değerlerine erişebilirsiniz.

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

Argümanlar reddedilmiyor mu? Yukarıdaki Ionut G. Stan'in önerisine bakın.
vikasde

vikasde haklı. argumentsBir işlev örneğinin özelliğine erişim kullanımdan kaldırıldı. Bkz. Developer.mozilla.org/tr/Core_JavaScript_1.5_Reference/…
Ionuț G. Stan

0

Oldukça kolay.

İlkinde kullanımdan kaldırıldı arguments.callee- çağrılan işleve başvuru. İkincisi, işlevinize bir referansınız varsa, bunların metinsel gösterimlerini kolayca alabilirsiniz. Üçüncü olarak işlevinizi yapıcı olarak çağırırsanız, ayrıcaObject.constructor aracılığıyla da bir bağlantınız olabilir. Not: İlk çözüm kullanımdan kaldırıldı, bu yüzden kullanamıyorsanız uygulama mimarinizi de düşünmelisiniz. Tam değişken isimlerine ihtiyacınız yoksa, sadece dahili bir fonksiyon değişkeninin içinde kullanınarguments sihir olmadan .

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

Hepsi bir yardım oluşturabilmemiz içinString ve re ile değiştirilecekler:

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

Bazı örnekler:

// 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 ile tadını çıkarın!

UPD: Jack Allan aslında biraz daha iyi bir çözüm sağladı. GJ Jack!


Bunun SomeFuncNameyerine, arguments.calleeher ikisi de işlev nesnesinin kendisini gösterirse, bu daha da basit olabilir .
Raphael Schweikert

0

Çözüm ne olursa olsun, aynı toString()derecede garip görünen garip işlevleri bozmamalıdır:

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

konsoldan ekran görüntüsü

Ayrıca, neden karmaşık düzenli ifadeler kullanılır? Bu şu şekilde yapılabilir:

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

Bu, her işlevde her yerde çalışır ve tek regex, .splithile nedeniyle tüm dizeyi bile işlemeyen beyaz boşluk kaldırmadır .


0

Tamam bu yüzden yeterli cevap bol eski bir soru. İşte boşluğu soyma görevi dışında, normal ifade kullanmayan teklifim. ("Strips_comments" fonksiyonu aslında onları fiziksel olarak kaldırmak yerine onları boşluk olduğunu unutmayın. Bunun nedeni başka bir yerde kullanmak ve çeşitli nedenlerle bozulmadan kalmak için orijinal yorum olmayan belirteçleri yerleri gerekir)

Bu yapıştırma işlemi mini bir test çerçevesi içerdiğinden oldukça uzun bir kod bloğudur.

    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

İşte bir yol:

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

Not: Bu yalnızca destekleyen tarayıcılarda çalışır arguments.callee.


2
While döngüsü, sağlanan kodla sonsuz bir döngüye neden olur (20171121at1047EDT'den itibaren)
George 2.0 Hope

@ George2.0Hope Bunu işaret ettiğiniz için teşekkürler. Cevabı güncelleyeceğim.
Ateş Goral

args.toSource bir işlev değildir (satır 20) ... ancak bunu şu şekilde değiştirseniz bile: console.log (args.toString ()); ... şunu elde edersiniz ... [object Object] ... yaparsanız daha iyi olur: console.log (JSON.stringify (args));
George 2.0 Hope

2009'dan bu yana işler biraz değişti!
Ateş Goral

1
herhangi birinin React için burada görünmesi durumunda, müthiş olan bu işlev katı modda çalışmaz.
Bireysel11

0

Not: En iyi çözümle ES6 parametre yıkımını kullanmak istiyorsanız, aşağıdaki satırı ekleyin.

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

-1

işlev parametre dize değeri görüntü dinamik olarak JSON . İtem.product_image2 bir URL dizesi olduğundan, changeImage inside parametresini çağırdığınızda onu tırnak içine almanız gerekir.

İşlevim Onclick

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

Benim fonksiyonu

<script type="text/javascript">
function changeImage(img)
 {
    document.getElementById("saleDetailDivGetImg").src=img;
    alert(img);
}
</script>
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.