Czy istnieje sposób na dynamiczne uzyskiwanie nazw parametrów funkcji?
Powiedzmy, że moja funkcja wygląda następująco:
function doSomething(param1, param2, .... paramN){
// fill an array with the parameter name and value
// some other code
}
W jaki sposób mógłbym uzyskać listę nazw parametrów i ich wartości do tablicy z wnętrza funkcji?
javascript
reflection
function-parameter
Vikasde
źródło
źródło
function doSomething(...args) { /*use args*/}
Odpowiedzi:
Poniższa funkcja zwróci tablicę nazw parametrów każdej przekazanej funkcji.
Przykładowe użycie:
Edytuj :
Dzięki wynalezieniu ES6 funkcja ta może zostać uruchomiona przy domyślnych parametrach. Oto szybki hack, który powinien działać w większości przypadków:
Mówię większość przypadków, ponieważ są pewne rzeczy, które mogą to potknąć
Edycja : Zauważam również, że vikasde chce również wartości parametrów w tablicy. Jest to już zapewnione w lokalnej zmiennej o nazwie argumenty.
fragment https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments :
Obiekt argumentów nie jest tablicą. Jest podobny do tablicy, ale nie ma żadnych właściwości tablicy poza długością. Na przykład nie ma metody pop. Można go jednak przekonwertować na prawdziwą macierz:
Jeśli dostępne są ogólne rodzaje tablic, można zamiast tego użyć następujących:
źródło
var fn = function(a /* fooled you)*/,b){};
spowoduje["a", "/*", "fooled", "you"]
Poniżej znajduje się kod pobrany z AngularJS, który wykorzystuje tę technikę dla mechanizmu wstrzykiwania zależności.
A oto wyjaśnienie tego zaczerpnięte z http://docs.angularjs.org/tutorial/step_05
źródło
annotate = angular.injector.$$annotate
Oto zaktualizowane rozwiązanie, które próbuje rozwiązać wszystkie wyżej wymienione przypadki krawędzi w kompaktowy sposób:
Skrócone wyjście testowe (pełne przypadki testowe załączono poniżej):
Pokaż fragment kodu
źródło
return (func+'') .replace(/[/][/].*$/mg,'') // strip single-line comments (line-ending sensitive, so goes first) .replace(/\s+/g,'') // remove whitespace
func + ''
zFunction.toString.call(func)
obrony przed przypadku, gdy funkcja ma zwyczaj .ToString () realizację..split(/\)[\{=]/, 1)[0]
({ a, b, c })
) na wszystkie parametry wewnątrz destrukcji. aby utrzymać nienaruszone obiekty w stanie nienaruszonym, zmień ostatni.split
na:.split(/,(?![^{]*})/g)
Rozwiązaniem, które jest mniej podatne na błędy i spacje, byłoby:
źródło
Wiele odpowiedzi tutaj zawiera wyrażenia regularne, jest w porządku, ale nie obsługuje zbyt dobrze nowych dodatków do języka (takich jak funkcje strzałek i klasy). Warto również zauważyć, że jeśli użyjesz którejkolwiek z tych funkcji na zminimalizowanym kodzie, to pójdzie 🔥. Będzie używać jakiejkolwiek zminimalizowanej nazwy. Angular omija ten problem, umożliwiając przekazywanie uporządkowanej tablicy ciągów, która odpowiada kolejności argumentów podczas rejestrowania ich w kontenerze DI. Tak dalej z rozwiązaniem:
To obsługuje oryginalny problem analizy i kilka innych typów funkcji (np. Funkcje strzałek). Oto pomysł na to, co może, a czego nie może obsłużyć w następujący sposób:
W zależności od tego, czego chcesz użyć do serwerów proxy ES6, najlepszym rozwiązaniem może być destrukcja. Na przykład, jeśli chcesz użyć go do wstrzykiwania zależności (używając nazw parametrów), możesz to zrobić w następujący sposób:
Nie jest to najbardziej zaawansowany resolver na rynku, ale daje wyobrażenie o tym, jak użyć proxy do obsługi go, jeśli chcesz użyć parsera args do prostej DI. Jest jednak jedno niewielkie zastrzeżenie w tym podejściu. Musimy użyć zadań restrukturyzacji zamiast normalnych parametrów. Kiedy mijamy proxy wtryskiwacza, destrukcja jest taka sama jak wywoływanie gettera na obiekcie.
To powoduje, że:
Jest podłączony do całej aplikacji. Najlepsze jest to, że aplikacja jest łatwa do przetestowania (możesz po prostu utworzyć instancję każdej klasy i przekazać makiety / odcinki / etc). Również jeśli chcesz zamienić implementacje, możesz to zrobić z jednego miejsca. Wszystko to jest możliwe dzięki obiektom JS Proxy.
Uwaga: Jest dużo pracy, którą należałoby wykonać, zanim będzie gotowy do użytku produkcyjnego, ale daje wyobrażenie o tym, jak by to wyglądało.
Trochę późno na odpowiedź, ale może pomóc innym, którzy myślą o tym samym. 👍
źródło
Wiem, że to stare pytanie, ale początkujący kopiowali to tak, jakby to była dobra praktyka w dowolnym kodzie. W większości przypadków konieczność przeanalizowania reprezentacji ciągu funkcji w celu użycia nazw parametrów po prostu ukrywa lukę w logice kodu.
Parametry funkcji są w rzeczywistości przechowywane w obiekcie tablicowym o nazwie
arguments
, gdzie pierwszy argument toarguments[0]
drugi,arguments[1]
i tak dalej. Wpisywanie nazw parametrów w nawiasach można traktować jako skróconą składnię. To:...jest taki sam jak:
Same zmienne są przechowywane w zakresie funkcji, a nie jako właściwości w obiekcie. Nie ma możliwości odzyskania nazwy parametru za pomocą kodu, ponieważ jest to jedynie symbol reprezentujący zmienną w języku ludzkim.
Zawsze rozważałem reprezentację ciągu funkcji jako narzędzia do celów debugowania, szczególnie ze względu na ten
arguments
obiekt podobny do tablicy. W pierwszej kolejności nie musisz podawać nazw argumentom. Jeśli spróbujesz parsować funkcję łańcuchową, tak naprawdę nie powie ci ona o dodatkowych parametrach nienazwanych, które może wziąć.Oto jeszcze gorsza i bardziej powszechna sytuacja. Jeśli funkcja ma więcej niż 3 lub 4 argumenty, logiczne może być przekazanie jej zamiast tego obiektu, co jest łatwiejsze w obsłudze.
W takim przypadku sama funkcja będzie w stanie odczytać otrzymany obiekt i poszukać jej właściwości oraz uzyskać zarówno ich nazwy, jak i wartości, ale próba przetworzenia reprezentacji ciągu funkcji dałaby tylko „obj” dla parametrów, co wcale nie jest przydatne.
źródło
Function.prototype.toString = function () { return 'function () { [native code] }'; };
Proxy
( pułapka konstruktora i pułapka zastosuj ) iReflection
do obsługi DI dla niektórych moich modułów. Jest dość solidny.Ponieważ JavaScript jest językiem skryptowym, uważam, że jego introspekcja powinna obsługiwać uzyskiwanie nazw parametrów funkcji. Pakowanie tej funkcjonalności stanowi naruszenie pierwszych zasad, dlatego postanowiłem zbadać ten problem dalej.
Doprowadziło mnie to do tego pytania, ale nie ma wbudowanych rozwiązań. Co doprowadziło mnie do tej odpowiedzi, która wyjaśnia, że
arguments
jest ona przestarzała poza funkcją, więc nie możemy już używaćmyFunction.arguments
lub otrzymujemy:Czas zakasać rękawy i zabrać się do pracy:
Parameters Pobieranie parametrów funkcji wymaga analizatora składni, ponieważ złożone wyrażenia, takie jak,
4*(5/3)
mogą być użyte jako wartości domyślne. Zatem odpowiedź Gaafara lub odpowiedź Jamesa Drew są jak dotąd najlepszymi podejściami.Próbowałem parserów babylon i esprima, ale niestety nie mogą one parsować samodzielnych anonimowych funkcji, jak wskazano w odpowiedzi Mateusza Charytoniuka . Znalazłem inne obejście, otaczając kod w nawiasach, aby nie zmieniać logiki:
Nowe wiersze zapobiegają problemom z
//
(komentarze jednowierszowe).⭐ Jeśli analizator składni nie jest dostępny, następną najlepszą opcją jest użycie sprawdzonej techniki, takiej jak wyrażenia regularne wtryskiwacza zależności Angular.js. Połączyłem funkcjonalną wersję odpowiedzi Lambder jest z odpowiedzią humbletim męska i dodano opcjonalny
ARROW
logiczną do kontrolowania, czy funkcje ES6 tłuszcz strzałek są akceptowane przez wyrażeń regularnych.Oto dwa rozwiązania, które zestawiłem. Zauważ, że nie mają one logiki do wykrycia, czy funkcja ma poprawną składnię, wyodrębniają tylko argumenty. Jest to na ogół w porządku, ponieważ zwykle przekazujemy przeanalizowane funkcje,
getArguments()
więc ich składnia jest już poprawna.Spróbuję dobrać te rozwiązania najlepiej jak potrafię, ale bez wysiłku opiekunów JavaScript, pozostanie to otwarty problem.
Wersja Node.js (nie działa, dopóki StackOverflow nie obsługuje Node.js):
Pełny przykład działania:
https://repl.it/repls/SandybrownPhonyAngles
Wersja przeglądarki (pamiętaj, że zatrzymuje się na pierwszej złożonej wartości domyślnej):
Pełny przykład działania:
https://repl.it/repls/StupendousShowyOffices
źródło
Przeczytałem większość odpowiedzi tutaj i chciałbym dodać moją linijkę.
lub
lub dla funkcji jednowierszowej w ECMA6
__
Powiedzmy, że masz funkcję
Poniższy kod powróci
"abc,def,ghi,jkl"
Ten kod będzie również działał z ustawieniem funkcji, którą dał Camilo Martin :
Również z komentarzem Buberssona do odpowiedzi Jacka Allana :
__
Wyjaśnienie
new RegExp('(?:'+Function.name+'\\s*|^)\\s*\\((.*?)\\)')
Tworzy to wyrażenie regularne z
new RegExp('(?:'+Function.name+'\\s*|^)\\s*\\((.*?)\\)')
. Muszę użyć,new RegExp
ponieważ wstrzykuję zmienną (Function.name
nazwę funkcji docelowej) do RegExp.Przykład Jeśli nazwa funkcji to „foo” (
function foo()
), RegExp będzie/foo\s*\((.*?)\)/
.Function.toString().replace(/\n/g, '')
Następnie konwertuje całą funkcję na ciąg znaków i usuwa wszystkie znaki nowej linii. Usunięcie nowej linii pomaga w konfiguracji funkcji, którą podarował Camilo Martin .
.exec(...)[1]
To jest
RegExp.prototype.exec
funkcja. Zasadniczo dopasowuje regularny wykładnik (new RegExp()
) do String (Function.toString()
). Następnie[1]
zwróci pierwszą grupę przechwytywania znalezioną w zwykłym wykładniku ((.*?)
)..replace(/\/\*.*?\*\//g, '').replace(/ /g, '')
Spowoduje to usunięcie wszelkich komentarz wnętrze
/*
i*/
, i usunąć wszystkie spacje.To także obsługuje teraz czytanie i rozumienie funkcji arrow (
=>
), takich jakf = (a, b) => void 0;
, w którychFunction.toString()
zwracane byłyby(a, b) => void 0
zamiast funkcji normalnychfunction f(a, b) { return void 0; }
. Oryginalne wyrażenie regularne spowodowałoby błąd w jego pomieszaniu, ale teraz jest rozliczane.Zmieniono z
new RegExp(Function.name+'\\s*\\((.*?)\\)')
(/Function\s*\((.*?)\)/
) nanew RegExp('(?:'+Function.name+'\\s*|^)\\((.*?)\\)')
(/(?:Function\s*|^)\((.*?)\)/
)Jeśli chcesz przekształcić wszystkie parametry w tablicę zamiast ciągu oddzielonego przecinkami, na końcu po prostu dodaj
.split(',')
.źródło
f = (a, b) => void 0
; nagetParameters(f)
otrzymujęTypeError: Cannot read property '1' of null
getParameters(a => b => c => d => a*b*c*d)
, które dzięki Twojemu kodowi nadal to dająTypeError: Cannot read property '1' of null
… podczas gdy ten działa stackoverflow.com/a/29123804=> [„a”, „b”, „c”]
źródło
Możesz także użyć parsera „esprima”, aby uniknąć wielu problemów z komentarzami, białymi znakami i innymi rzeczami na liście parametrów.
Działa nawet z takim kodem:
źródło
Próbowałem to zrobić wcześniej, ale nigdy nie znalazłem praktycznego sposobu, aby to zrobić. Skończyło się na tym, że podałem przedmiot, a potem go zapętliłem.
źródło
Nie wiem, czy to rozwiązanie pasuje do twojego problemu, ale pozwala ci na nowo zdefiniować dowolną funkcję, bez konieczności zmiany kodu, który z niej korzysta. Istniejące wywołania wykorzystują parametry pozycjonowane, podczas gdy implementacja funkcji może używać „nazwanych parametrów” (pojedynczy parametr skrótu).
Pomyślałem, że i tak zmodyfikujesz istniejące definicje funkcji, więc dlaczego nie mieć funkcji fabrycznej, która robi dokładnie to, czego chcesz:
Mam nadzieję, że to pomoże.
źródło
Właściwym sposobem na to jest użycie parsera JS. Oto przykład z użyciem żołędzi .
Tutaj kod znajduje nazwy trzech (formalnych) parametrów funkcji
f
. Czyni to poprzez karmienief
sięacorn.parse()
.źródło
Nie wiem, jak uzyskać listę parametrów, ale możesz to zrobić, aby uzyskać ich liczbę.
źródło
function gotcha (a, b = false, c) {}; alert(gotcha.length)
Biorąc odpowiedź od @ jack-allan zmodyfikowałem nieco funkcję, aby umożliwić domyślne właściwości ES6, takie jak:
wciąż wracać
[ 'a', 'b' ]
źródło
Jak zwykle to robię:
Możesz nawet odwoływać argumenty według nazw funkcji takich jak:
Mam nadzieję że to pomoże!
źródło
var args = name.arguments; console.log('I WANNa SEE', args);
wypisał coś w stylu „{arg1: {...}, arg2: 'string'}”? To może wyczyścić rzeczy:(function fn (arg, argg, arrrrgggg) { console.log('#fn:', fn.arguments, Object.keys(fn.arguments)); }); fn('Huh...?', 'Wha...?', 'Magic...?');
. Argumenty funkcji są obiektami podobnymi do „macierzy” i mają wyliczalne indeksy. Nie sądzę, aby mapowanie skrótów było możliwe, ale możesz po prostu przekazać literał obiektowy, w którym jest to dobra praktyka, jeśli i tak masz więcej niż 4 parametry.źródło
Odpowiedź na to wymaga 3 kroków:
argValues
). Jest to proste, ponieważ będzie dostępne jakoarguments
funkcja.argNames
). Nie jest to tak łatwe i wymaga analizy funkcji. Zamiast samodzielnie wykonywać złożone wyrażenia regularne i martwić się przypadkami krawędzi (parametry domyślne, komentarze, ...), możesz użyć biblioteki takiej jak Babilon, która przekształci funkcję w abstrakcyjne drzewo składni, z którego można uzyskać nazwy parametrów.Kod będzie taki
i zalogowany obiekt będzie
A oto działający przykład https://tonicdev.com/5763eb77a945f41300f62a79/5763eb77a945f41300f62a7a
źródło
źródło
Wow, już tyle odpowiedzi. Jestem pewien, że to zostanie pochowane. Mimo to pomyślałem, że może to być przydatne dla niektórych.
Nie byłem w pełni zadowolony z wybranych odpowiedzi, ponieważ w ES6 nie działa dobrze z wartościami domyślnymi. I nie zapewnia również informacji o wartości domyślnej. Chciałem też mieć lekką funkcję, która nie zależy od zewnętrznej biblioteki.
Ta funkcja jest bardzo przydatna do celów debugowania, na przykład: rejestrowanie wywoływanej funkcji za pomocą jej parametrów, domyślnych wartości parametrów i argumentów.
Wczoraj spędziłem trochę czasu na łamaniu odpowiedniego RegExp, aby rozwiązać ten problem i oto co wymyśliłem. Działa bardzo dobrze i jestem bardzo zadowolony z wyniku:
Jak można powiedzieć, niektóre nazwy parametrów znikają, ponieważ transpilator Babel usuwa je z funkcji. Jeśli uruchomisz to w najnowszym NodeJS, działa on zgodnie z oczekiwaniami (skomentowane wyniki pochodzą z NodeJS).
Inna uwaga, jak stwierdzono w komentarzu, jest taka, że nie działa ona z wbudowanymi funkcjami strzałek jako wartością domyślną. To po prostu sprawia, że wyciąganie wartości za pomocą RegExp jest zbyt skomplikowane.
Daj mi znać, jeśli było to dla Ciebie przydatne! Bardzo chciałbym usłyszeć opinie!
źródło
Dam ci krótki przykład poniżej:
źródło
Ten pakiet używa przekształcenia w celu utworzenia AST, a następnie nazwy parametrów są zbierane z nich, co pozwala na obsługę dopasowania wzorca, domyślnych argumentów, funkcji strzałek i innych funkcji ES6.
https://www.npmjs.com/package/es-arguments
źródło
Zmodyfikowałem wersję pobraną z AngularJS, która implementuje mechanizm wstrzykiwania zależności do pracy bez Angulara. Zaktualizowałem również
STRIP_COMMENTS
wyrażenie regularne do pracyECMA6
, więc obsługuje takie rzeczy, jak wartości domyślne w podpisie.źródło
Dostęp do wartości argumentów przekazywanych do funkcji można uzyskać za pomocą właściwości „arguments”.
źródło
arguments
właściwości instancji funkcji jest przestarzały. Zobacz developer.mozilla.org/en/Core_JavaScript_1.5_Reference/…To całkiem proste.
Na początku jest przestarzałe
arguments.callee
- odniesienie do wywoływanej funkcji. Po drugie, jeśli masz odniesienie do swojej funkcji, możesz łatwo uzyskać ich reprezentację tekstową. Po trzecie, jeśli wywołujesz swoją funkcję jako konstruktor, możesz również mieć link poprzez yourObject.constructor. Uwaga: pierwsze rozwiązanie jest przestarzałe, więc jeśli nie możesz go nie używać, musisz także pomyśleć o architekturze aplikacji. Jeśli nie potrzebujesz dokładnych nazw zmiennych, po prostu użyj wewnątrz zmiennej wewnętrznej funkcjiarguments
bez żadnej magii.https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/arguments/callee
Wszyscy będą dzwonić doString i zamieniać na re, abyśmy mogli stworzyć pomocnika:
Kilka przykładów:
Ciesz się z JS!
UPD: Jack Allan otrzymał trochę lepsze rozwiązanie. GJ Jack!
źródło
SomeFuncName
zamiastarguments.callee
(oba wskazują na sam obiekt funkcji).Niezależnie od rozwiązania, nie może działać na dziwne funkcje, których
toString()
wygląd jest równie dziwny:Ponadto, po co używać złożonych wyrażeń regularnych? Można to zrobić w następujący sposób:
Działa to wszędzie z każdą funkcją, a jedynym wyrażeniem regularnym jest usuwanie białych znaków, które nawet nie przetwarzają całego łańcucha ze względu na
.split
podstęp.źródło
Ok, więc stare pytanie z dużą ilością odpowiednich odpowiedzi. oto moja oferta, która nie używa wyrażenia regularnego, z wyjątkiem zwykłego zadania usuwania białych znaków. (Powinienem zauważyć, że funkcja „strips_comments” faktycznie rozdziela je, zamiast fizycznie je usuwać. To dlatego, że używam go gdzie indziej i z różnych powodów potrzebuję położenia oryginalnych tokenów bez komentarza, aby pozostały nienaruszone)
Jest to dość długi blok kodu, ponieważ wklejenie zawiera szkielet mini testu.
źródło
Oto jeden ze sposobów:
Uwaga: Działa to tylko w przeglądarkach, które obsługują
arguments.callee
.źródło
Uwaga: jeśli chcesz użyć restrukturyzacji parametrów ES6 z najlepszym rozwiązaniem, dodaj następujący wiersz.
źródło
funkcja parametr łańcuch wartości obrazu dynamicznie z JSON . Ponieważ item.product_image2 jest ciągiem adresu URL, musisz umieścić go w cudzysłowach, gdy wywołasz parametr changeImage wewnątrz.
Moja funkcja Onclick
Moja funkcja
źródło