Czy jest możliwe utworzenie ciągu szablonu jako zwykłego ciągu?
let a="b:${b}";
a następnie przekonwertuj go na łańcuch szablonu
let b=10;
console.log(a.template());//b:10
bez eval
, new Function
i inne środki dynamicznego generowania kodu?
javascript
ecmascript-6
eval
template-strings
KOLANICH
źródło
źródło
Odpowiedzi:
Ponieważ ciąg szablonu musi pobierać odwołanie do
b
zmiennej dynamicznie (w czasie wykonywania), więc odpowiedź brzmi: NIE, nie da się obejść bez dynamicznego generowania kodu.Ale
eval
to całkiem proste:źródło
a
ciągu i będzie znacznie mniej niebezpieczny:let tpl = eval('`'+a.replace(/`/g,'\\`')+'`');
. Myślę, że ważniejsze jest to, abyeval
uniemożliwić kompilatorowi optymalizację kodu. Ale myślę, że nie ma to związku z tym pytaniem.eval
. Należy jednak pamiętać, że literał szablonu sam w sobie jest formąeval
. Dwa przykłady: var test =Result: ${alert('hello')}
; var test =Result: ${b=4}
; Oba kończą się wykonaniem dowolnego kodu w kontekście skryptu. Jeśli chcesz zezwolić na dowolne ciągi znaków, równie dobrze możesz zezwolićeval
.W moim projekcie stworzyłem coś takiego w ES6:
UPDATE Usunąłem zależność lodash, ES6 ma równoważne metody pobierania kluczy i wartości.
źródło
ReferenceError: _ is not defined
. Czy jest to kod nie ES6, alelodash
konkretny, czy ...?O co tu prosisz:
jest dokładnie równoważne (pod względem mocy i, hm, bezpieczeństwa) do
eval
: możliwości pobrania łańcucha zawierającego kod i wykonania tego kodu; a także możliwość zobaczenia przez wykonywany kod zmiennych lokalnych w środowisku wywołującego.W JS nie ma możliwości, aby funkcja mogła zobaczyć zmienne lokalne w swoim programie wywołującym, chyba że ta funkcja jest
eval()
. NawetFunction()
nie mogę tego zrobić.Kiedy słyszysz, że w JavaScript pojawia się coś, co nazywa się „ciągami szablonów”, naturalne jest założenie, że jest to wbudowana biblioteka szablonów, taka jak Mustache. Tak nie jest. To głównie interpolacja ciągów znaków i ciągi wielowierszowe dla JS. Myślę jednak, że przez jakiś czas będzie to powszechne nieporozumienie. :(
źródło
template is not a function
.Nie, nie da się tego zrobić bez dynamicznego generowania kodu.
Jednak stworzyłem funkcję, która zamieni zwykły ciąg znaków w funkcję, do której można dostarczyć mapę wartości, używając wewnętrznie ciągów szablonów.
Wygeneruj streszczenie ciągu szablonów
Stosowanie:
Mam nadzieję, że to komuś pomoże. Jeśli znajdziesz problem z kodem, prosimy o aktualizację Gist.
źródło
var test = generateTemplateString('/api/${param1}/${param2}/')
console.log(test({param1: 'bar', param2: 'foo'}))
zwrotu szablonów/api/bar//
TLDR: https://jsfiddle.net/w3jx07vt/
Wydaje się, że wszyscy martwią się o dostęp do zmiennych, dlaczego po prostu ich nie przekazać? Jestem pewien, że nie będzie zbyt trudno uzyskać kontekst zmiennej w programie wywołującym i przekazać go. Użyj tego https://stackoverflow.com/a/6394168/6563504, aby pobrać props z obj. Nie mogę teraz testować dla ciebie, ale to powinno działać.
Przetestowany. Oto pełny kod.
źródło
${}
znaków. Spróbuj:/(?!\${)([^{}]*)(?=})/g
Problem polega na tym, aby mieć funkcję, która ma dostęp do zmiennych swojego wywołującego. Właśnie dlatego widzimy bezpośrednie
eval
wykorzystanie do przetwarzania szablonów. Możliwym rozwiązaniem byłoby wygenerowanie funkcji pobierającej parametry formalne nazwane przez właściwości słownika i wywołującej ją z odpowiednimi wartościami w tej samej kolejności. Alternatywnym sposobem byłoby posiadanie czegoś prostego:A dla każdego używającego kompilatora Babel musimy stworzyć zamknięcie, które zapamięta środowisko, w którym zostało utworzone:
źródło
eval
dlatego, że działa tylko zename
zmienną globalnąvar template = function() { var name = "John Smith"; var message = "Hello, my name is ${name}"; this.local = new Function('return
;')();}
new Function
nie ma dostępu dovar name
wtemplate
funkcji.Istnieje wiele dobrych rozwiązań, ale jeszcze żadne nie wykorzystywałoby metody ES6 String.raw . Oto moja inicjatywa. Ma ważne ograniczenie polegające na tym, że akceptuje tylko właściwości z przekazanego obiektu, co oznacza, że żadne wykonanie kodu w szablonie nie zadziała.
parts: ["Hello, ", "! Are you ", " years old?"]
args: ["name", "age"]
obj
według nazwy właściwości. Rozwiązanie jest ograniczone przez płytkie mapowanie jednopoziomowe. Niezdefiniowane wartości są zastępowane pustym łańcuchem, ale akceptowane są inne fałszywe wartości.parameters: ["John Doe", 18]
String.raw(...)
i zwróć wynik.źródło
.replace()
wielokrotnego dzwonienia ?.replace()
:) Uważam, że czytelność jest ważna, więc sam używając wyrażeń regularnych staram się je nazywać, żeby wszystko toPodobna do odpowiedzi Daniela (i s.meijer za GIST ), ale bardziej czytelny:
Uwaga: To nieznacznie poprawia oryginał s.meijer, ponieważ nie będzie pasował do rzeczy takich jak
${foo{bar}
(wyrażenie regularne dopuszcza tylko znaki niekręcone w nawiasach klamrowych wewnątrz${
i}
).AKTUALIZACJA: Poproszono mnie o przykład za pomocą tego, więc proszę:
źródło
/\$\{(.*?)(?!\$\{)\}/g
(do obsługi nawiasów klamrowych). Mam działające rozwiązanie, ale nie jestem pewien, czy jest tak przenośne, jak Twoje, więc chciałbym zobaczyć, jak należy to zaimplementować na stronie. Mój też używaeval()
.eval
pozostawia cię bardziej otwartym na możliwe błędy, które mogłyby spowodować problemy z bezpieczeństwem, podczas gdy moja wersja szuka właściwości obiektu ze ścieżki oddzielonej kropkami, która powinna być bezpieczna.Możesz na przykład użyć prototypu łańcucha
Ale odpowiedź na pierwotne pytanie nie jest możliwa.
źródło
Spodobała mi się odpowiedź s.meijera i na jego podstawie napisałem własną wersję:
źródło
Wymagałem tej metody z obsługą Internet Explorera. Okazało się, że tylne tiki nie są obsługiwane nawet przez IE11. Również; używanie
eval
lub odpowiednikFunction
nie wydaje się właściwe.Dla tego, który zauważy; Używam również znaków odwrotnych, ale te są usuwane przez kompilatory, takie jak babel. Metody sugerowane przez inne metody zależą od nich w czasie wykonywania. Jak powiedziano wcześniej; jest to problem w IE11 i niższych.
Oto co wymyśliłem:
Przykładowe dane wyjściowe:
źródło
eval('`' + taggedURL + '`')
po prostu nie działa.eval
. Odnośnie do literałów szablonu: dzięki za ponowne zwrócenie uwagi. Używam Babel do transpozycji mojego kodu, ale moja funkcja najwyraźniej nadal nie działa 😐Obecnie nie mogę komentować istniejących odpowiedzi, więc nie mogę bezpośrednio skomentować doskonałej odpowiedzi Bryana Raynora. Tak więc ta odpowiedź będzie aktualizować jego odpowiedź, wprowadzając niewielką korektę.
Krótko mówiąc, jego funkcja nie potrafi w rzeczywistości buforować utworzonej funkcji, więc zawsze zostanie odtworzona, niezależnie od tego, czy wcześniej widziała szablon. Oto poprawiony kod:
źródło
@Mateusz Moska, rozwiązanie działa świetnie, ale gdy użyłem go w React Native (tryb budowania), wyświetla błąd: Nieprawidłowy znak '' ' , chociaż działa, gdy uruchamiam go w trybie debugowania.
Zapisałem więc własne rozwiązanie za pomocą wyrażenia regularnego.
Demo: https://es6console.com/j31pqx1p/
UWAGA: Ponieważ nie znam głównej przyczyny problemu, zgłosiłem zgłoszenie w repozytorium react-native, https://github.com/facebook/react-native/issues/14107 , aby gdy mogli napraw / poprowadź mnie o tym samym :)
źródło
Wciąż dynamiczny, ale wydaje się bardziej kontrolowany niż zwykłe używanie nagiego evala:
https://repl.it/IdVt/3
źródło
To rozwiązanie działa bez ES6:
Uwaga:
Function
konstruktor jest zawsze tworzony w zasięgu globalnym, co może potencjalnie spowodować nadpisanie zmiennych globalnych przez szablon, np.render("hello ${ someGlobalVar = 'some new value' }", {name:'mo'});
źródło
Ponieważ wymyślamy na nowo koło w czymś, co byłoby uroczą funkcją w javascript.
Używam
eval()
, co nie jest bezpieczne, ale javascript nie jest bezpieczny. Chętnie przyznaję, że nie jestem doskonały z javascriptem, ale miałem potrzebę i potrzebowałem odpowiedzi, więc ją zrobiłem.Zdecydowałem się stylizować moje zmienne na
@
raczej niż an$
, szczególnie dlatego, że chcę używać funkcji multilinii w literałach bez oceniania, aż będzie gotowa. Tak więc składnia zmiennej to@{OptionalObject.OptionalObjectN.VARIABLE_NAME}
Nie jestem ekspertem od javascript, więc chętnie skorzystam z porad dotyczących ulepszeń, ale ...
Następuje bardzo prosta implementacja
W mojej rzeczywistej realizacji decyduję się na użycie
@{{variable}}
. Jeszcze jeden zestaw szelek. Absurdalnie mało prawdopodobne, aby spotkał się z tym niespodziewanie. Wyglądałoby to następująco/\@\{\{(.*?)(?!\@\{\{)\}\}/g
Aby było to łatwiejsze do odczytania
Jeśli nie masz doświadczenia z wyrażeniem regularnym, całkiem bezpieczną zasadą jest unikanie każdego znaku niealfanumerycznego i nie zawsze niepotrzebnie unikaj litery, ponieważ wiele takich znaków ma specjalne znaczenie dla praktycznie wszystkich odmian wyrażenia regularnego.
źródło
Powinieneś wypróbować ten mały moduł JS autorstwa Andrei Giammarchi z github: https://github.com/WebReflection/backtick-template
Demo (wszystkie poniższe testy zwracają prawdę):
źródło
Stworzyłem własne rozwiązanie, wykonując typ z opisem jako funkcją
i tak robiąc:
źródło
Zamiast używać eval, lepiej jest użyć wyrażenia regularnego
Eval nie jest zalecane i bardzo odradzane, więc nie używaj go ( mdn eval ).
źródło