Gibt es eine Möglichkeit, die Funktionsparameternamen einer Funktion dynamisch abzurufen?
Angenommen, meine Funktion sieht folgendermaßen aus:
function doSomething(param1, param2, .... paramN){
// fill an array with the parameter name and value
// some other code
}
Wie würde ich nun eine Liste der Parameternamen und ihrer Werte in ein Array innerhalb der Funktion einfügen?
javascript
reflection
function-parameter
vikasde
quelle
quelle
function doSomething(...args) { /*use args*/}
Antworten:
Die folgende Funktion gibt ein Array der Parameternamen aller übergebenen Funktionen zurück.
Anwendungsbeispiel:
Bearbeiten :
Mit der Erfindung von ES6 kann diese Funktion durch Standardparameter ausgelöst werden. Hier ist ein kurzer Hack, der in den meisten Fällen funktionieren sollte:
Ich sage die meisten Fälle, weil es einige Dinge gibt, die es auslösen werden
Bearbeiten : Ich stelle auch fest, dass vikasde die Parameterwerte auch in einem Array haben möchte. Dies ist bereits in einer lokalen Variablen mit dem Namen argument enthalten.
Auszug aus https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments :
Das Argumentobjekt ist kein Array. Es ähnelt einem Array, hat jedoch keine Array-Eigenschaften außer der Länge. Zum Beispiel hat es nicht die Pop-Methode. Es kann jedoch in ein echtes Array konvertiert werden:
Wenn Array-Generika verfügbar sind, können Sie stattdessen Folgendes verwenden:
quelle
var fn = function(a /* fooled you)*/,b){};
führt zu["a", "/*", "fooled", "you"]
Unten finden Sie den Code von AngularJS, der die Technik für seinen Abhängigkeitsinjektionsmechanismus verwendet.
Und hier ist eine Erklärung davon aus http://docs.angularjs.org/tutorial/step_05
quelle
annotate = angular.injector.$$annotate
Hier ist eine aktualisierte Lösung, die versucht, alle oben genannten Randfälle auf kompakte Weise anzugehen:
Verkürzte Testausgabe (vollständige Testfälle sind unten beigefügt):
Code-Snippet anzeigen
quelle
return (func+'') .replace(/[/][/].*$/mg,'') // strip single-line comments (line-ending sensitive, so goes first) .replace(/\s+/g,'') // remove whitespace
func + ''
mitFunction.toString.call(func)
verteidigen gegen den Fall , wenn die Funktion eine benutzerdefinierte .toString () Umsetzung hat..split(/\)[\{=]/, 1)[0]
({ a, b, c })
) in alle Parameter innerhalb der Destrukturierung aufgeteilt..split
.split(/,(?![^{]*})/g)
Eine Lösung, die weniger fehleranfällig für Leerzeichen und Kommentare ist, wäre:
quelle
Viele der Antworten hier verwenden reguläre Ausdrücke. Dies ist in Ordnung, behandelt jedoch neue Ergänzungen der Sprache nicht so gut (wie Pfeilfunktionen und Klassen). Es ist auch zu beachten, dass, wenn Sie eine dieser Funktionen für minimierten Code verwenden, diese gehen wird 🔥. Es wird verwendet, was auch immer der minimierte Name ist. Angular umgeht dies, indem Sie ein geordnetes Array von Zeichenfolgen übergeben können, das der Reihenfolge der Argumente entspricht, wenn Sie sie im DI-Container registrieren. Also weiter mit der Lösung:
Dies behandelt das ursprüngliche Analyseproblem und einige weitere Funktionstypen (z. B. Pfeilfunktionen). Hier ist eine Vorstellung davon, was es so kann und was nicht:
Je nachdem, was Sie für ES6-Proxies verwenden möchten, ist die Destrukturierung möglicherweise die beste Wahl. Wenn Sie es beispielsweise für die Abhängigkeitsinjektion verwenden möchten (unter Verwendung der Namen der Parameter), können Sie dies wie folgt tun:
Es ist nicht der fortschrittlichste Resolver auf dem Markt, aber es gibt eine Vorstellung davon, wie Sie einen Proxy verwenden können, um damit umzugehen, wenn Sie den Args-Parser für einfache DI verwenden möchten. Es gibt jedoch eine kleine Einschränkung bei diesem Ansatz. Wir müssen Destrukturierungszuweisungen anstelle normaler Parameter verwenden. Wenn wir den Injektor-Proxy übergeben, entspricht die Destrukturierung dem Aufruf des Getters für das Objekt.
Dies gibt Folgendes aus:
Es verkabelt die gesamte Anwendung. Das Beste daran ist, dass die App einfach zu testen ist (Sie können einfach jede Klasse instanziieren und Mocks / Stubs / etc übergeben). Auch wenn Sie Implementierungen austauschen müssen, können Sie dies von einem einzigen Ort aus tun. All dies ist aufgrund von JS-Proxy-Objekten möglich.
Hinweis: Es muss noch viel Arbeit geleistet werden, bevor es für die Produktion bereit ist, aber es gibt eine Vorstellung davon, wie es aussehen würde.
Es ist ein bisschen spät in der Antwort, aber es kann anderen helfen, die an das Gleiche denken. 👍
quelle
Ich weiß, dass dies eine alte Frage ist, aber Anfänger haben dies kopypastiert, als ob dies in jedem Code eine gute Praxis wäre. In den meisten Fällen verbirgt das Parsen der Zeichenfolgendarstellung einer Funktion zur Verwendung ihrer Parameternamen nur einen Fehler in der Logik des Codes.
Parameter einer Funktion werden tatsächlich in einem Array-ähnlichen Objekt namens aufgerufen
arguments
, wobei das erste Argumentarguments[0]
das zweite, das zweitearguments[1]
usw. ist. Das Schreiben von Parameternamen in Klammern kann als Kurzsyntax angesehen werden. Dies:...ist das gleiche wie:
Die Variablen selbst werden im Funktionsumfang gespeichert, nicht als Eigenschaften in einem Objekt. Es gibt keine Möglichkeit, den Parameternamen über Code abzurufen, da es sich lediglich um ein Symbol handelt, das die Variable in der menschlichen Sprache darstellt.
Ich habe die Zeichenfolgendarstellung einer Funktion immer als Werkzeug für Debugging-Zwecke betrachtet, insbesondere aufgrund dieses
arguments
Array-ähnlichen Objekts. Sie müssen den Argumenten zunächst keine Namen geben. Wenn Sie versuchen, eine String-Funktion zu analysieren, werden Sie nicht über zusätzliche unbenannte Parameter informiert, die möglicherweise erforderlich sind.Hier ist eine noch schlimmere und häufigere Situation. Wenn eine Funktion mehr als 3 oder 4 Argumente hat, kann es logisch sein, ihr stattdessen ein Objekt zu übergeben, mit dem einfacher gearbeitet werden kann.
In diesem Fall kann die Funktion selbst das empfangene Objekt lesen, nach seinen Eigenschaften suchen und sowohl ihre Namen als auch ihre Werte abrufen. Wenn Sie jedoch versuchen, die Zeichenfolgendarstellung der Funktion zu analysieren, erhalten Sie nur "obj" für Parameter. das ist überhaupt nicht nützlich.
quelle
Function.prototype.toString = function () { return 'function () { [native code] }'; };
Proxy
( Konstruktor-Trap und Apply- Trap ) und verwendeReflection
DI für einige meiner Module. Es ist ziemlich solide.Da JavaScript eine Skriptsprache ist, sollte die Selbstbeobachtung das Abrufen von Funktionsparameternamen unterstützen. Das Punting auf diese Funktionalität ist ein Verstoß gegen die ersten Prinzipien, deshalb habe ich beschlossen, das Problem weiter zu untersuchen.
Das führte mich zu dieser Frage, aber keine eingebauten Lösungen. Was mich dazu geführt hat dieser Antwort führte, die erklärt, dass sie
arguments
nur außerhalb der Funktion veraltet ist, sodass wir sie nicht mehr verwenden könnenmyFunction.arguments
oder erhalten:Zeit, die Ärmel hochzukrempeln und an die Arbeit zu gehen:
⭐ Das Abrufen von Funktionsparametern erfordert einen Parser, da komplexe Ausdrücke wie
4*(5/3)
als Standardwerte verwendet werden können. So Gaafar Antwort oder James Drew Antwort sind bisher die besten Ansätze.Ich habe das Babylon ausprobiert und Esprima- Parser ausprobiert, aber leider können sie keine eigenständigen anonymen Funktionen analysieren, wie in Mateusz Charytoniuks Antwort ausgeführt . Ich habe jedoch eine andere Problemumgehung gefunden, indem ich den Code in Klammern gesetzt habe, um die Logik nicht zu ändern:
Die Zeilenumbrüche verhindern Probleme mit
//
(einzeiligen Kommentaren).⭐ Wenn kein Parser verfügbar ist, besteht die nächstbeste Option darin, eine bewährte Technik wie die regulären Ausdrücke des Abhängigkeitsinjektors von Angular.js zu verwenden. Ich habe eine funktionale Version von Lambders Antwort mit kombiniert der Antwort und einen optionalen
ARROW
Booleschen Wert hinzugefügt, um zu steuern, ob die regulären Ausdrücke ES6- Fettpfeilfunktionen zulassen.Hier sind zwei Lösungen, die ich zusammengestellt habe. Beachten Sie, dass diese keine Logik haben, um festzustellen, ob eine Funktion eine gültige Syntax hat. Sie extrahieren nur die Argumente. Dies ist im Allgemeinen in Ordnung, da wir normalerweise analysierte Funktionen an übergeben,
getArguments()
sodass deren Syntax bereits gültig ist.Ich werde versuchen, diese Lösungen so gut wie möglich zu kuratieren, aber ohne die Bemühungen der JavaScript-Betreuer bleibt dies ein offenes Problem.
Node.js-Version (kann erst ausgeführt werden, wenn StackOverflow Node.js unterstützt):
Vollständiges Arbeitsbeispiel:
https://repl.it/repls/SandybrownPhonyAngles
Browserversion (beachten Sie, dass sie beim ersten komplexen Standardwert stoppt):
Vollständiges Arbeitsbeispiel:
https://repl.it/repls/StupendousShowyOffices
quelle
Ich habe die meisten Antworten hier gelesen und möchte meinen Einzeiler hinzufügen.
oder
oder für eine Einzeilerfunktion in ECMA6
__ __
Angenommen, Sie haben eine Funktion
Der folgende Code wird zurückgegeben
"abc,def,ghi,jkl"
Dieser Code funktioniert auch mit der Einrichtung einer Funktion, die Camilo Martin gegeben hat:
Auch mit Buberssons Kommentar zu Jack Allans Antwort :
__ __
Erläuterung
new RegExp('(?:'+Function.name+'\\s*|^)\\s*\\((.*?)\\)')
Dies erzeugt einen regulären Ausdruck mit dem
new RegExp('(?:'+Function.name+'\\s*|^)\\s*\\((.*?)\\)')
. Ich muss verwenden,new RegExp
weil ich eine Variable (Function.name
den Namen der Funktion, auf die abgezielt wird) in RegExp einfüge.Beispiel Wenn der Funktionsname "foo" (
function foo()
) lautet, lautet RegExp/foo\s*\((.*?)\)/
.Function.toString().replace(/\n/g, '')
Dann konvertiert es die gesamte Funktion in eine Zeichenfolge und entfernt alle Zeilenumbrüche. Das Entfernen von Zeilenumbrüchen hilft bei der Funktionseinstellung, die Camilo Martin gegeben hat.
.exec(...)[1]
Dies ist die
RegExp.prototype.exec
Funktion. Grundsätzlich wird der reguläre Exponent (new RegExp()
) mit dem String (Function.toString()
) abgeglichen . Dann gibt das[1]
die erste Erfassungsgruppe zurück, die im regulären Exponenten ((.*?)
) gefunden wurde..replace(/\/\*.*?\*\//g, '').replace(/ /g, '')
Dadurch werden alle Kommentare in
/*
und entfernt*/
und alle Leerzeichen entfernt.Dies hat auch jetzt unterstützt das Lesen und Verstehen Pfeil (
=>
) Funktionen, wie zum Beispielf = (a, b) => void 0;
, in welchemFunction.toString()
zurückkehren würde(a, b) => void 0
anstelle der normalen Funktion desfunction f(a, b) { return void 0; }
. Der ursprüngliche reguläre Ausdruck hätte einen Fehler in seiner Verwirrung ausgelöst, wird aber jetzt berücksichtigt.Die Änderung erfolgte von
new RegExp(Function.name+'\\s*\\((.*?)\\)')
(/Function\s*\((.*?)\)/
) nachnew RegExp('(?:'+Function.name+'\\s*|^)\\((.*?)\\)')
(/(?:Function\s*|^)\((.*?)\)/
)Wenn Sie alle Parameter in ein Array anstatt in einen durch Kommas getrennten String umwandeln möchten, fügen Sie am Ende einfach hinzu
.split(',')
.quelle
f = (a, b) => void 0
; aufgetParameters(f)
ichTypeError: Cannot read property '1' of null
getParameters(a => b => c => d => a*b*c*d)
, die mit Ihrem Code immer noch das ergebenTypeError: Cannot read property '1' of null
... während dieser funktioniert stackoverflow.com/a/29123804=> ["a", "b", "c"]
quelle
Sie können auch den Parser "esprima" verwenden, um viele Probleme mit Kommentaren, Leerzeichen und anderen Dingen in der Parameterliste zu vermeiden.
Es funktioniert sogar mit Code wie diesem:
quelle
Ich habe es schon einmal versucht, aber nie einen praktischen Weg gefunden, dies zu erreichen. Ich habe stattdessen ein Objekt übergeben und es dann durchlaufen.
quelle
Ich weiß nicht, ob diese Lösung zu Ihrem Problem passt, aber Sie können damit jede gewünschte Funktion neu definieren, ohne den Code ändern zu müssen, der sie verwendet. Bestehende Aufrufe verwenden positionierte Parameter, während die Funktionsimplementierung möglicherweise "benannte Parameter" (einen einzelnen Hash-Parameter) verwendet.
Ich dachte, dass Sie ohnehin vorhandene Funktionsdefinitionen ändern werden. Warum also nicht eine Factory-Funktion, die genau das macht, was Sie wollen:
Ich hoffe es hilft.
quelle
Der richtige Weg, dies zu tun, ist die Verwendung eines JS-Parsers. Hier ist ein Beispiel mit Eichel .
Der Code hier findet die Namen der drei (formalen) Parameter der Funktion
f
. Er tut dies durch die Fütterungf
inacorn.parse()
.quelle
Ich weiß nicht, wie ich eine Liste der Parameter erhalten soll, aber Sie können dies tun, um zu ermitteln, wie viele erwartet werden.
quelle
function gotcha (a, b = false, c) {}; alert(gotcha.length)
Ausgehend von der Antwort von @ jack-allan habe ich die Funktion leicht geändert, um ES6-Standardeigenschaften wie die folgenden zuzulassen:
immer noch zurückkehren
[ 'a', 'b' ]
quelle
Wie ich es normalerweise mache:
Sie können die Argumente sogar anhand des Funktionsnamens wie folgt referenzieren:
Hoffe das hilft!
quelle
var args = name.arguments; console.log('I WANNa SEE', args);
etwas wie "{arg1: {...}, arg2: 'string'}" ausgeben? Dies könnte die Dinge klären :(function fn (arg, argg, arrrrgggg) { console.log('#fn:', fn.arguments, Object.keys(fn.arguments)); }); fn('Huh...?', 'Wha...?', 'Magic...?');
. Funktionsargumente sind ein 'Array'-ähnliches Objekt mit aufzählbaren Indizes. Ich denke nicht, dass eine Hash-Zuordnung möglich ist, aber Sie könnten einfach ein Objekt-Literal übergeben, das eine gute Praxis ist, wenn Sie sowieso mehr als 4 Parameter haben.quelle
Die Antwort darauf erfordert 3 Schritte:
argValues
). Dies ist unkompliziert, da esarguments
innerhalb der Funktion verfügbar sein wird .argNames
). Dies ist nicht so einfach und erfordert das Parsen der Funktion. Anstatt den komplexen regulären Ausdruck selbst durchzuführen und sich um Randfälle (Standardparameter, Kommentare, ...) zu kümmern, können Sie eine Bibliothek wie babylon verwenden, die die Funktion in einen abstrakten Syntaxbaum analysiert, aus dem Sie die Namen der Parameter abrufen können.Der Code wird so sein
und das protokollierte Objekt wird sein
Und hier ist ein funktionierendes Beispiel: https://tonicdev.com/5763eb77a945f41300f62a79/5763eb77a945f41300f62a7a
quelle
quelle
Wow, so viele Antworten schon. Ich bin mir ziemlich sicher, dass dies begraben wird. Trotzdem dachte ich, dass dies für einige nützlich sein könnte.
Ich war mit den ausgewählten Antworten nicht ganz zufrieden, da es in ES6 mit Standardwerten nicht gut funktioniert. Außerdem werden die Standardwertinformationen nicht bereitgestellt. Ich wollte auch eine leichte Funktion, die nicht von einer externen Bibliothek abhängt.
Diese Funktion ist sehr nützlich für Debugging-Zwecke, zum Beispiel: Protokollierung der aufgerufenen Funktion mit ihren Parametern, Standardparameterwerten und Argumenten.
Ich habe gestern einige Zeit damit verbracht, das richtige RegExp zu knacken, um dieses Problem zu lösen, und das habe ich mir ausgedacht. Es funktioniert sehr gut und ich bin sehr zufrieden mit dem Ergebnis:
Wie Sie sehen können, verschwinden einige der Parameternamen, weil der Babel-Transpiler sie aus der Funktion entfernt. Wenn Sie dies im neuesten NodeJS ausführen würden, funktioniert es wie erwartet (Die kommentierten Ergebnisse stammen von NodeJS).
Ein weiterer Hinweis, wie im Kommentar angegeben, ist, dass er nicht mit Inline-Pfeilfunktionen als Standardwert funktioniert. Dies macht es einfach viel zu komplex, die Werte mit einem RegExp zu extrahieren.
Bitte lassen Sie mich wissen, ob dies für Sie nützlich war! Würde gerne ein Feedback hören!
quelle
Ich werde Ihnen unten ein kurzes Beispiel geben:
quelle
Dieses Paket verwendet eine Neufassung, um einen AST zu erstellen, und anschließend werden die Parameternamen daraus gesammelt. Dadurch kann der Mustervergleich, Standardargumente, Pfeilfunktionen und andere ES6-Funktionen unterstützt werden.
https://www.npmjs.com/package/es-arguments
quelle
Ich habe die Version von AngularJS geändert , die einen Abhängigkeitsinjektionsmechanismus implementiert, um ohne Angular zu funktionieren. Ich habe auch den
STRIP_COMMENTS
regulären Ausdruck für die Arbeit aktualisiertECMA6
, sodass er beispielsweise Standardwerte in der Signatur unterstützt.quelle
Sie können auf die an eine Funktion übergebenen Argumentwerte mit der Eigenschaft "arguments" zugreifen.
quelle
arguments
Eigenschaft einer Funktionsinstanz ist veraltet. Siehe developer.mozilla.org/en/Core_JavaScript_1.5_Reference/…Es ist ziemlich einfach.
Am ersten gibt es eine veraltete
arguments.callee
- einen Verweis auf die aufgerufene Funktion. Im zweiten Schritt, wenn Sie einen Verweis auf Ihre Funktion haben, können Sie leicht deren Textdarstellung erhalten. Beim dritten Aufruf Ihrer Funktion als Konstruktor können Sie auch einen Link über yourObject.constructor haben. NB: Die erste Lösung ist veraltet. Wenn Sie sie nicht verwenden können, müssen Sie auch über Ihre App-Architektur nachdenken. Wenn Sie keine genauen Variablennamen benötigen, verwenden Sie diese einfach innerhalb einer funktionalen internen Variablenarguments
ohne Magie.https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/arguments/callee
Alle werden toString aufrufen und durch re ersetzen, damit wir einen Helfer erstellen können:
Einige Beispiele:
Viel Spaß mit JS!
UPD: Jack Allan wurde tatsächlich eine etwas bessere Lösung angeboten. GJ Jack!
quelle
SomeFuncName
anstelle von verwendenarguments.callee
(beide zeigen auf das Funktionsobjekt selbst).Was auch immer die Lösung sein mag, es darf nicht bei seltsamen Funktionen brechen, deren
toString()
Aussehen genauso seltsam ist:Warum sollten Sie auch komplexe reguläre Ausdrücke verwenden? Dies kann wie folgt erfolgen:
Dies funktioniert überall mit jeder Funktion, und der einzige reguläre Ausdruck ist das Entfernen von Leerzeichen, bei dem aufgrund des
.split
Tricks nicht einmal die gesamte Zeichenfolge verarbeitet wird .quelle
Ok, also eine alte Frage mit vielen angemessenen Antworten. Hier ist mein Angebot, das keinen regulären Ausdruck verwendet, außer für die einfache Aufgabe, Leerzeichen zu entfernen. (Ich sollte beachten, dass die Funktion "strips_comments" sie tatsächlich ausräumt, anstatt sie physisch zu entfernen. Das liegt daran, dass ich sie an anderer Stelle verwende und aus verschiedenen Gründen die Positionen der ursprünglichen Token ohne Kommentar benötigen, um intakt zu bleiben.)
Es ist ein ziemlich langer Codeblock, da dieses Einfügen ein Mini-Test-Framework enthält.
quelle
Hier ist eine Möglichkeit:
Hinweis: Dies funktioniert nur in Browsern, die dies unterstützen
arguments.callee
.quelle
Hinweis: Wenn Sie die Destrukturierung von ES6-Parametern mit der Top-Lösung verwenden möchten, fügen Sie die folgende Zeile hinzu.
quelle
Funktionsparameter Zeichenfolge Wert Bild dynamisch aus JSON . Da item.product_image2 eine URL-Zeichenfolge ist, müssen Sie sie in Anführungszeichen setzen, wenn Sie den Parameter changeImage aufrufen.
Meine Funktion Klicken Sie auf
Meine Funktion
quelle