¿Es posible crear una cadena de plantilla como una cadena habitual?
let a="b:${b}";
y luego convertirlo en una cadena de plantilla
let b=10;
console.log(a.template());//b:10
sin eval
, new Function
y otros medios de generación de código dinámico?
javascript
ecmascript-6
eval
template-strings
KOLANICH
fuente
fuente
Respuestas:
Como la cadena de su plantilla debe obtener referencia a la
b
variable dinámicamente (en tiempo de ejecución), la respuesta es: NO, es imposible hacerlo sin la generación dinámica de código.Pero con
eval
esto es bastante simple:fuente
a
cadena y que será mucho menos insegura:let tpl = eval('`'+a.replace(/`/g,'\\`')+'`');
. Creo que lo más importante eseval
evitar que el compilador optimice su código. Pero creo que es irrelevante para esta pregunta.eval
. Sin embargo, recuerde que una plantilla literal es en sí misma una forma deeval
. Dos ejemplos: prueba var =Result: ${alert('hello')}
; prueba var =Result: ${b=4}
; Ambos terminarán ejecutando código arbitrario en el contexto del script. Si desea permitir cadenas arbitrarias, también puede permitireval
.En mi proyecto, he creado algo como esto con ES6:
ACTUALIZACIÓN He eliminado la dependencia lodash, ES6 tiene métodos equivalentes para obtener claves y valores.
fuente
ReferenceError: _ is not defined
. ¿Es el código no ES6 sinolodash
específico, o ...?Lo que estás pidiendo aquí:
es exactamente equivalente (en términos de potencia y, por ejemplo, seguridad) a
eval
: la capacidad de tomar una cadena que contiene código y ejecutar ese código; y también la capacidad del código ejecutado para ver variables locales en el entorno del llamante.No hay forma en JS para que una función vea variables locales en su llamador, a menos que esa función sea
eval()
. InclusoFunction()
no puedo hacerlo.Cuando escuche que hay algo llamado "cadenas de plantillas" en JavaScript, es natural suponer que es una biblioteca de plantillas incorporada, como Moustache. No lo es Es principalmente solo interpolación de cadenas y cadenas multilíneas para JS. Sin embargo, creo que esto será un error común durante un tiempo. :(
fuente
template is not a function
.No, no hay forma de hacerlo sin la generación dinámica de código.
Sin embargo, he creado una función que convertirá una cadena normal en una función que se puede proporcionar con un mapa de valores, utilizando cadenas de plantilla internamente.
Generar plantilla de cadena Gist
Uso:
Espero que esto ayude a alguien. Si encuentra un problema con el código, sea tan amable de actualizar el Gist.
fuente
var test = generateTemplateString('/api/${param1}/${param2}/')
console.log(test({param1: 'bar', param2: 'foo'}))
devolución de plantillas/api/bar//
TLDR: https://jsfiddle.net/w3jx07vt/
Todo el mundo parece estar preocupado por acceder a las variables, ¿por qué no simplemente pasarlas? Estoy seguro de que no será demasiado difícil obtener el contexto variable en la persona que llama y transmitirlo. Use este https://stackoverflow.com/a/6394168/6563504 para obtener los accesorios de obj. No puedo hacerte una prueba en este momento, pero esto debería funcionar.
Probado Aquí está el código completo.
fuente
${}
caracteres. Prueba:/(?!\${)([^{}]*)(?=})/g
El problema aquí es tener una función que tenga acceso a las variables de su llamador. Es por eso que vemos que
eval
se usa directamente para el procesamiento de plantillas. Una posible solución sería generar una función tomando parámetros formales nombrados por las propiedades de un diccionario y llamándolo con los valores correspondientes en el mismo orden. Una forma alternativa sería tener algo simple como esto:Y para cualquiera que use el compilador de Babel, necesitamos crear un cierre que recuerde el entorno en el que se creó:
fuente
eval
porque funciona solo con unaname
variable globalvar template = function() { var name = "John Smith"; var message = "Hello, my name is ${name}"; this.local = new Function('return
'+ mensaje +';')();}
new Function
no tiene acceso avar name
latemplate
función.Hay muchas buenas soluciones publicadas aquí, pero todavía no hay ninguna que utilice el método ES6 String.raw . Aquí está mi contribución. Tiene una limitación importante porque solo aceptará propiedades de un objeto pasado, lo que significa que no funcionará la ejecución de código en la plantilla.
parts: ["Hello, ", "! Are you ", " years old?"]
args: ["name", "age"]
obj
por nombre de propiedad. La solución está limitada por un mapeo superficial de un nivel. Los valores no definidos se sustituyen con una cadena vacía, pero se aceptan otros valores falsos.parameters: ["John Doe", 18]
String.raw(...)
y devolver el resultado.fuente
.replace()
repetidamente?.replace()
, sin embargo :) Creo que la lectura es importante, por lo que durante el uso de expresiones regulares a mí mismo, trato de nombrarlos para ayudar a hacer sentido a todo ...Similar a la respuesta de Daniel (y la esencia de mi mejer ) pero más legible:
Nota: Esto mejora ligeramente el original de s.meijer, ya que no coincidirá con cosas como
${foo{bar}
(la expresión regular solo permite caracteres de llaves no rizadas dentro${
y}
).ACTUALIZACIÓN: Me pidieron un ejemplo usando esto, así que aquí tienes:
fuente
/\$\{(.*?)(?!\$\{)\}/g
(para manejar llaves de nido). Tengo una solución que funciona, pero no estoy seguro de que sea tan portátil como la suya, por lo que me encantaría ver cómo se debe implementar en una página. El mío también usaeval()
.eval
te deja mucho más abierto a posibles errores que causarían problemas de seguridad, mientras que todo lo que mi versión está haciendo es buscar una propiedad en un objeto desde una ruta separada por puntos, lo que debería ser seguro.Puede usar el prototipo de cadena, por ejemplo
Pero la respuesta de la pregunta original es de ninguna manera.
fuente
Me gustó la respuesta de s.meijer y escribí mi propia versión basada en la suya:
fuente
Requerí este método con soporte para Internet Explorer. Resultó que los ticks posteriores no son compatibles ni siquiera con IE11. También; usando
eval
o es equivalenteFunction
no se siente bien.Para el que nota; También uso backticks, pero estos son eliminados por compiladores como babel. Los métodos sugeridos por otros dependen de ellos en el tiempo de ejecución. Como se dijo antes; Este es un problema en IE11 y versiones anteriores.
Entonces esto es lo que se me ocurrió:
Salida de ejemplo:
fuente
eval('`' + taggedURL + '`')
simplemente no funciona.eval
. Con respecto a los literales de plantilla: gracias por señalarlo nuevamente. Estoy usando Babel para transpilar mi código, pero aparentemente mi función todavía no funcionará 😐Actualmente no puedo comentar sobre las respuestas existentes, así que no puedo comentar directamente sobre la excelente respuesta de Bryan Raynor. Por lo tanto, esta respuesta va a actualizar su respuesta con una ligera corrección.
En resumen, su función no puede en realidad almacenar en caché la función creada, por lo que siempre se recreará, independientemente de si se ha visto la plantilla antes. Aquí está el código corregido:
fuente
@Mateusz Moska, la solución funciona muy bien, pero cuando la usé en React Native (modo de compilación), arroja un error: Carácter no válido '' ' , aunque funciona cuando lo ejecuto en modo de depuración.
Entonces escribí mi propia solución usando regex.
Demostración: https://es6console.com/j31pqx1p/
NOTA: Como no conozco la causa raíz de un problema, planteé un ticket en el repositorio react-native, https://github.com/facebook/react-native/issues/14107 , para que una vez que puedan arreglar / guiarme por lo mismo :)
fuente
Todavía dinámico, pero parece más controlado que simplemente usando una evaluación desnuda:
https://repl.it/IdVt/3
fuente
Esta solución funciona sin ES6:
Nota: el
Function
constructor siempre se crea en el ámbito global, lo que podría hacer que la plantilla sobrescriba las variables globales, p. Ej.render("hello ${ someGlobalVar = 'some new value' }", {name:'mo'});
fuente
Ya que estamos reinventando la rueda en algo que sería una característica encantadora en javascript.
Yo uso
eval()
, que no es seguro, pero JavaScript no es seguro. Admito que no soy excelente con javascript, pero tenía una necesidad y necesitaba una respuesta, así que hice una.Elegí estilizar mis variables con un en
@
lugar de un$
, particularmente porque quiero usar la función multilínea de literales sin evaluar hasta que esté lista. Entonces la sintaxis variable es@{OptionalObject.OptionalObjectN.VARIABLE_NAME}
No soy un experto en JavaScript, por lo que con gusto tomaría consejos sobre mejoras pero ...
Sigue una implementación muy simple
En mi implementación real, elijo usar
@{{variable}}
. Un juego más de llaves. Absurdamente improbable encontrar eso inesperadamente. La expresión regular para eso se vería así/\@\{\{(.*?)(?!\@\{\{)\}\}/g
Para que sea más fácil de leer
Si no tiene experiencia con expresiones regulares, una regla bastante segura es escapar de todos los caracteres no alfanuméricos, y nunca escapar innecesariamente de las letras, ya que muchas letras tienen un significado especial para prácticamente todos los sabores de expresiones regulares.
fuente
Deberías probar este pequeño módulo JS, de Andrea Giammarchi, de github: https://github.com/WebReflection/backtick-template
Demostración (todas las siguientes pruebas son verdaderas):
fuente
Hice mi propia solución haciendo un tipo con una descripción como función
y al hacerlo:
fuente
En lugar de usar eval mejor es usar regex
Eval no es recomendable y altamente desaconsejado, así que no lo use ( mdn eval ).
fuente