É possível criar uma sequência de modelos como uma sequência usual
let a="b:${b}";
e depois converta-o em uma string de modelo
let b=10;
console.log(a.template());//b:10
sem eval
, new Function
e outros meios de geração de código dinâmico?
javascript
ecmascript-6
eval
template-strings
KOLANICH
fonte
fonte
Respostas:
Como a sequência do modelo deve obter referência à
b
variável dinamicamente (em tempo de execução), a resposta é: NÃO, é impossível ficar sem a geração dinâmica de código.Mas com
eval
isso é bem simples:fonte
a
corda e será muito menos inseguros:let tpl = eval('`'+a.replace(/`/g,'\\`')+'`');
. Eu acho que o mais importante é queeval
evite que o compilador otimize seu código. Mas acho irrelevante para esta pergunta.eval
. No entanto, lembre-se de que um literal de modelo é uma forma deeval
. Dois exemplos: var test =Result: ${alert('hello')}
; teste var =Result: ${b=4}
; Ambos acabarão executando código arbitrário no contexto do script. Se você deseja permitir seqüências arbitrárias, também pode permitireval
.No meu projeto, criei algo assim com o ES6:
ATUALIZAÇÃO Removi a dependência do lodash, o ES6 possui métodos equivalentes para obter chaves e valores.
fonte
ReferenceError: _ is not defined
. O código não é ES6, maslodash
específico ou ...?O que você está pedindo aqui:
é exatamente equivalente (em termos de poder e, er, segurança) à
eval
: capacidade de pegar uma string que contém código e executar esse código; e também a capacidade do código executado ver variáveis locais no ambiente do chamador.Na JS, não há como uma função ver variáveis locais em seu chamador, a menos que essa função seja
eval()
. MesmoFunction()
não pode fazer isso.Quando você ouve que algo chamado "strings de modelo" está chegando ao JavaScript, é natural assumir que é uma biblioteca de modelos embutida, como o Bigode. Não é. É principalmente apenas interpolação de cadeias e cadeias de linhas múltiplas para JS. Eu acho que isso vai ser um equívoco comum por um tempo, no entanto. :(
fonte
template is not a function
.Não, não há uma maneira de fazer isso sem a geração dinâmica de código.
No entanto, criei uma função que transformará uma sequência regular em uma função que pode ser fornecida com um mapa de valores, usando seqüências de caracteres de modelo internamente.
Gerar essência da sequência de modelos
Uso:
Espero que isso ajude alguém. Se você encontrar um problema com o código, por favor, atualize o Gist.
fonte
var test = generateTemplateString('/api/${param1}/${param2}/')
console.log(test({param1: 'bar', param2: 'foo'}))
retornados/api/bar//
TLDR: https://jsfiddle.net/w3jx07vt/
Todo mundo parece estar preocupado em acessar variáveis, por que não apenas passá-las? Tenho certeza de que não será muito difícil obter o contexto variável no chamador e transmiti-lo. Use este https://stackoverflow.com/a/6394168/6563504 para obter os adereços da obj. Não posso testar para você agora, mas isso deve funcionar.
Testado. Aqui está o código completo.
fonte
${}
caracteres. Tente:/(?!\${)([^{}]*)(?=})/g
O problema aqui é ter uma função que tenha acesso às variáveis de seu chamador. É por isso que vemos o uso direto
eval
no processamento de modelos. Uma solução possível seria gerar uma função usando parâmetros formais nomeados pelas propriedades de um dicionário e chamando-a com os valores correspondentes na mesma ordem. Uma maneira alternativa seria ter algo simples como este:E para quem usa o compilador Babel, precisamos criar um fechamento que se lembre do ambiente em que foi criado:
fonte
eval
porque ele funciona apenas com a globalname
variávelvar template = function() { var name = "John Smith"; var message = "Hello, my name is ${name}"; this.local = new Function('return
'+ message +';')();}
new Function
não tem acesso aovar name
natemplate
função.Há muitas boas soluções publicadas aqui, mas nenhuma ainda utiliza o método ES6 String.raw . Aqui está minha contriubuição. Ele tem uma limitação importante, pois aceita apenas propriedades de um objeto passado, o que significa que nenhuma execução de código no modelo funcionará.
parts: ["Hello, ", "! Are you ", " years old?"]
args: ["name", "age"]
obj
do nome da propriedade. A solução é limitada pelo mapeamento superficial de um nível. Valores indefinidos são substituídos por uma sequência vazia, mas outros valores falsos são aceitos.parameters: ["John Doe", 18]
String.raw(...)
e retorne o resultado.fonte
.replace()
repetidamente?.replace()
, no entanto :) Eu acho que a legibilidade é importante, então, enquanto eu mesmo uso expressões regulares, tento nomeá-las para ajudar a entender tudo ...Semelhante à resposta de Daniel (e à essência do s.meijer ), mas mais legível:
Nota: Isso melhora um pouco o original do s.meijer, já que não corresponde a coisas como
${foo{bar}
(a regex permite apenas caracteres entre chaves não encaracolados dentro${
e}
).UPDATE: Me pediram um exemplo usando isso, então aqui está:
fonte
/\$\{(.*?)(?!\$\{)\}/g
(para manipular chaves de ninho). Eu tenho uma solução funcional, mas não tenho certeza de que seja tão portátil quanto a sua, então adoraria ver como isso deve ser implementado em uma página. O meu também usaeval()
.eval
deixa você muito mais aberto a possíveis erros que causariam problemas de segurança, enquanto tudo que minha versão está fazendo é procurar uma propriedade em um objeto a partir de um caminho separado por pontos, o que deve ser seguro.Você pode usar o protótipo de cadeia, por exemplo
Mas a resposta da pergunta original não é possível.
fonte
Gostei da resposta do s.meijer e escrevi minha própria versão com base na dele:
fonte
Eu exigi esse método com suporte para o Internet Explorer. Acontece que os ticks anteriores não são suportados pelo IE11. Além disso; usando
eval
ou equivalenteFunction
não parece certo.Para aquele que percebe; Eu também uso backticks, mas esses são removidos por compiladores como o babel. Os métodos sugeridos por outros dependem deles no tempo de execução. Como dito antes; esse é um problema no IE11 e inferior.
Então é isso que eu criei:
Exemplo de saída:
fonte
eval('`' + taggedURL + '`')
simplesmente não funciona.eval
. Em relação aos literais do modelo: obrigado por apontar isso novamente. Eu estou usando Babel para transpilar meu código, mas minha função ainda não funcionará aparentemente #Atualmente, não posso comentar sobre as respostas existentes, por isso não posso comentar diretamente sobre a excelente resposta de Bryan Raynor. Portanto, essa resposta atualizará sua resposta com uma leve correção.
Em resumo, sua função falha ao armazenar em cache a função criada; portanto, ela sempre será recriada, independentemente de ter visto o modelo antes. Aqui está o código corrigido:
fonte
@Mateusz Moska, a solução funciona muito bem, mas quando a usei no React Native (modo de compilação), gera um erro: Caractere inválido '' ' , embora funcione quando a executo no modo de depuração.
Então, eu escrevi minha própria solução usando regex.
Demonstração: https://es6console.com/j31pqx1p/
NOTA: Como não sei a causa raiz de um problema, aumentei um ticket no repositório react-native, https://github.com/facebook/react-native/issues/14107 , para que assim que possível corrigir / orientar-me sobre o mesmo :)
fonte
Ainda dinâmico, mas parece mais controlado do que apenas usar uma avaliação nua:
https://repl.it/IdVt/3
fonte
Esta solução funciona sem o ES6:
Nota: o
Function
construtor é sempre criado no escopo global, o que pode potencialmente fazer com que variáveis globais sejam substituídas pelo modelo, por exemplo,render("hello ${ someGlobalVar = 'some new value' }", {name:'mo'});
fonte
Como estamos reinventando a roda em algo que seria um recurso adorável em javascript.
Eu uso
eval()
, o que não é seguro, mas o javascript não é seguro. Eu admito prontamente que não sou excelente com javascript, mas tinha uma necessidade e precisava de uma resposta, então fiz uma.Eu escolhi estilizar minhas variáveis com um
@
e não um$
, principalmente porque eu quero usar o recurso de múltiplas linhas de literais sem avaliar até que esteja pronto. Portanto, a sintaxe variável é@{OptionalObject.OptionalObjectN.VARIABLE_NAME}
Como não sou especialista em javascript, gostaria de receber conselhos sobre melhorias, mas ...
Uma implementação muito simples segue
Na minha implementação real, eu escolho usar
@{{variable}}
. Mais um conjunto de chaves. É absurdamente improvável encontrar isso inesperadamente. O regex para isso seria semelhante/\@\{\{(.*?)(?!\@\{\{)\}\}/g
Para facilitar a leitura
Se você não tem experiência com regex, uma regra bastante segura é escapar a todos os caracteres não alfanuméricos e nunca escapar desnecessariamente de letras, pois muitas letras escapadas têm um significado especial para praticamente todos os tipos de regex.
fonte
Você deve experimentar este pequeno módulo JS, de Andrea Giammarchi, do github: https://github.com/WebReflection/backtick-template
Demo (todos os seguintes testes retornam true):
fonte
Eu fiz minha própria solução fazendo um tipo com uma descrição em função
e assim fazendo:
fonte
Em vez de usar eval melhor, use regex
Eval não é recomendado e altamente desencorajado, portanto, não o use ( mdn eval ).
fonte