Est-il possible de créer une chaîne de modèle comme une chaîne habituelle
let a="b:${b}";
puis le convertir en chaîne de modèle
let b=10;
console.log(a.template());//b:10
sans eval
, new Function
et d'autres moyens de génération de code dynamique?
javascript
ecmascript-6
eval
template-strings
KOLANICH
la source
la source
Réponses:
Comme votre chaîne de modèle doit obtenir une référence à la
b
variable de manière dynamique (au moment de l'exécution), la réponse est: NON, il est impossible de se passer de la génération de code dynamique.Mais avec
eval
c'est assez simple:la source
a
chaîne et il sera beaucoup moins précaire:let tpl = eval('`'+a.replace(/`/g,'\\`')+'`');
. Je pense que le plus important est d'eval
empêcher le compilateur d'optimiser votre code. Mais je pense que cela n'a aucun rapport avec cette question.eval
. Cependant, rappelez-vous qu'un modèle littéral est lui-même une forme deeval
. Deux exemples: var test =Result: ${alert('hello')}
; test var =Result: ${b=4}
; Les deux finiront par exécuter du code arbitraire dans le contexte du script. Si vous souhaitez autoriser des chaînes arbitraires, vous pouvez aussi bien autorisereval
.Dans mon projet, j'ai créé quelque chose comme ça avec ES6:
MISE À JOUR J'ai supprimé la dépendance de lodash, ES6 a des méthodes équivalentes pour obtenir des clés et des valeurs.
la source
ReferenceError: _ is not defined
. N'est-ce pas du code ES6 maislodash
spécifique, ou ...?Ce que vous demandez ici:
est exactement équivalent (en termes de puissance et, euh, de sécurité) à
eval
: la capacité de prendre une chaîne contenant du code et d'exécuter ce code; et aussi la possibilité pour le code exécuté de voir les variables locales dans l'environnement de l'appelant.Il n'y a aucun moyen dans JS pour une fonction de voir les variables locales dans son appelant, à moins que cette fonction ne le soit
eval()
.Function()
Je ne peux même pas le faire.Lorsque vous entendez qu'il y a quelque chose appelé "chaînes de modèles" venant de JavaScript, il est naturel de supposer qu'il s'agit d'une bibliothèque de modèles intégrée, comme Moustache. Ça ne l'est pas. Il s'agit principalement d'une interpolation de chaînes et de chaînes multilignes pour JS. Je pense cependant que ce sera une idée fausse courante pendant un certain temps. :(
la source
template is not a function
.Non, il n'y a pas moyen de faire cela sans génération de code dynamique.
Cependant, j'ai créé une fonction qui transformera une chaîne régulière en une fonction qui peut être fournie avec une carte de valeurs, en utilisant des chaînes de modèle en interne.
Générer la liste de chaînes de modèle
Usage:
J'espère que cela aide quelqu'un. Si vous rencontrez un problème avec le code, veuillez mettre à jour le Gist.
la source
var test = generateTemplateString('/api/${param1}/${param2}/')
console.log(test({param1: 'bar', param2: 'foo'}))
retour de modèles/api/bar//
TLDR: https://jsfiddle.net/w3jx07vt/
Tout le monde semble s'inquiéter d'accéder aux variables, pourquoi ne pas simplement les transmettre? Je suis sûr qu'il ne sera pas trop difficile d'obtenir le contexte variable dans l'appelant et de le transmettre. Utilisez ce https://stackoverflow.com/a/6394168/6563504 pour obtenir les accessoires de obj. Je ne peux pas tester pour vous pour le moment, mais cela devrait fonctionner.
Testé. Voici le code complet.
la source
${}
caractères. Essayez:/(?!\${)([^{}]*)(?=})/g
Le problème ici est d'avoir une fonction qui a accès aux variables de son appelant. C'est pourquoi nous voyons l'utilisation directe
eval
pour le traitement des modèles. Une solution possible serait de générer une fonction prenant des paramètres formels nommés par les propriétés d'un dictionnaire, et l'appelant avec les valeurs correspondantes dans le même ordre. Une autre manière serait d'avoir quelque chose de simple comme ceci:Et pour quiconque utilise le compilateur Babel, nous devons créer une fermeture qui se souvient de l'environnement dans lequel il a été créé:
la source
eval
parce qu'il ne fonctionne qu'avec unename
variable globalevar template = function() { var name = "John Smith"; var message = "Hello, my name is ${name}"; this.local = new Function('return
'+ message +';')();}
new Function
n'a pas accès àvar name
latemplate
fonction.Il existe de nombreuses bonnes solutions publiées ici, mais aucune n'utilise encore la méthode ES6 String.raw . Voici ma contribution. Il a une limitation importante en ce qu'il n'acceptera que les propriétés d'un objet passé, ce qui signifie qu'aucune exécution de code dans le modèle ne fonctionnera.
parts: ["Hello, ", "! Are you ", " years old?"]
args: ["name", "age"]
obj
par nom de propriété. La solution est limitée par une cartographie à un niveau peu profonde. Les valeurs non définies sont remplacées par une chaîne vide, mais d'autres valeurs erronées sont acceptées.parameters: ["John Doe", 18]
String.raw(...)
et renvoyer le résultat.la source
.replace()
plusieurs reprises?.replace()
, cependant :) Je pense que la lisibilité est importante, donc tout en utilisant moi-même des expressions régulières, j'essaie de les nommer pour aider à comprendre tout cela ...Similaire à la réponse de Daniel (et à l' essentiel de mon collègue ) mais plus lisible:
Remarque: Cela améliore légèrement l'original de s.meijer, car il ne correspondra pas à des choses comme
${foo{bar}
(le regex autorise uniquement les caractères non bouclés à l'intérieur${
et}
).MISE À JOUR: On m'a demandé un exemple d'utilisation de ceci, alors voilà:
la source
/\$\{(.*?)(?!\$\{)\}/g
(pour gérer les accolades nidifiées). J'ai une solution qui fonctionne mais je ne suis pas sûr qu'elle soit aussi portable que la vôtre, alors j'aimerais voir comment cela devrait être implémenté dans une page. Le mien utilise égalementeval()
.eval
vous laisse beaucoup plus ouvert à d'éventuelles erreurs qui pourraient causer des problèmes de sécurité, alors que ma version ne fait que rechercher une propriété sur un objet à partir d'un chemin séparé par des points, ce qui devrait être sûr.Vous pouvez utiliser le prototype de chaîne, par exemple
Mais la réponse à la question initiale n'est pas possible.
la source
J'ai aimé la réponse de mon collègue et j'ai écrit ma propre version basée sur la sienne:
la source
J'avais besoin de cette méthode avec le support d'Internet Explorer. Il s'est avéré que les tiques arrière ne sont même pas prises en charge par IE11. Aussi; utiliser
eval
ou son équivalentFunction
ne semble pas correct.Pour celui qui remarque; J'utilise également des backticks, mais ceux-ci sont supprimés par des compilateurs comme babel. Les méthodes suggérées par d'autres dépendent d'elles au moment de l'exécution. Comme dit précédemment; il s'agit d'un problème dans IE11 et les versions antérieures.
Voici donc ce que j'ai proposé:
Exemple de sortie:
la source
eval('`' + taggedURL + '`')
ne fonctionne tout simplement pas.eval
. En ce qui concerne les modèles littéraux: merci de le souligner à nouveau. J'utilise Babel pour transpiler mon code mais ma fonction ne fonctionnera toujours pas apparemment 😐Je ne peux actuellement pas commenter les réponses existantes, donc je ne peux pas commenter directement l'excellente réponse de Bryan Raynor. Ainsi, cette réponse va mettre à jour sa réponse avec une légère correction.
En bref, sa fonction ne parvient pas à mettre en cache la fonction créée, elle sera donc toujours recréée, qu'elle ait ou non vu le modèle auparavant. Voici le code corrigé:
la source
@Mateusz Moska, la solution fonctionne très bien, mais quand je l'ai utilisée dans React Native (mode construction), elle génère une erreur: caractère invalide '' ' , bien que cela fonctionne lorsque je l'exécute en mode débogage.
J'ai donc écrit ma propre solution en utilisant regex.
Démo: https://es6console.com/j31pqx1p/
REMARQUE: comme je ne connais pas la cause profonde d'un problème, j'ai soulevé un ticket dans le repo react-native, https://github.com/facebook/react-native/issues/14107 , afin qu'une fois qu'ils puissent corrige / guide-moi à peu près pareil :)
la source
Toujours dynamique mais semble plus contrôlé que la simple utilisation d'une évaluation nue:
https://repl.it/IdVt/3
la source
Cette solution fonctionne sans ES6:
Remarque: le
Function
constructeur est toujours créé dans la portée globale, ce qui pourrait entraîner l'écrasement des variables globales par le modèle, par exemplerender("hello ${ someGlobalVar = 'some new value' }", {name:'mo'});
la source
Puisque nous réinventons la roue sur quelque chose qui serait une fonctionnalité intéressante en javascript.
j'utilise
eval()
, ce qui n'est pas sécurisé, mais javascript n'est pas sécurisé. J'avoue volontiers que je ne suis pas excellent avec javascript, mais j'avais un besoin, et j'avais besoin d'une réponse alors j'en ai fait une.J'ai choisi de styliser mes variables avec un
@
plutôt qu'un$
, en particulier parce que je veux utiliser la fonctionnalité multiligne des littéraux sans évaluer jusqu'à ce qu'elle soit prête. La syntaxe variable est donc@{OptionalObject.OptionalObjectN.VARIABLE_NAME}
Je ne suis pas un expert javascript, donc je prendrais volontiers des conseils d'amélioration mais ...
Une implémentation très simple suit
Dans ma mise en œuvre réelle, je choisis d'utiliser
@{{variable}}
. Un autre ensemble d'accolades. Il est peu probable de rencontrer cela de manière inattendue. Le regex pour cela ressemblerait à/\@\{\{(.*?)(?!\@\{\{)\}\}/g
Pour rendre cela plus facile à lire
Si vous n'êtes pas expérimenté avec les regex, une règle assez sûre est d'échapper à tous les caractères non alphanumériques, et de ne jamais échapper inutilement aux lettres, car de nombreuses lettres échappées ont une signification spéciale pour pratiquement toutes les saveurs de regex.
la source
Vous devriez essayer ce petit module JS, par Andrea Giammarchi, de github: https://github.com/WebReflection/backtick-template
Démo (tous les tests suivants retournent vrai):
la source
J'ai fait ma propre solution en faisant un type avec une description comme fonction
et ainsi de suite:
la source
Au lieu d'utiliser eval, mieux vaut utiliser pour regex
Eval n'est pas recommandé et fortement déconseillé, veuillez donc ne pas l'utiliser ( mdn eval ).
la source