Javascript 将字符串转换为模板字符串
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29182244/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Convert a string to a template string
提问by KOLANICH
Is it possible to create a template string as a usual string
是否可以将模板字符串创建为通常的字符串
let a="b:${b}";
an then convert it into a template string
然后将其转换为模板字符串
let b=10;
console.log(a.template());//b:10
without eval, new Functionand other means of dynamic code generation?
没有eval,new Function和其他动态代码生成方式?
采纳答案by alexpods
As your template string must get reference to the bvariable dynamicly (in runtime), so the answer is: NO, it impossible to do without dynamic code generation.
由于您的模板字符串必须b动态地(在运行时)获得对变量的引用,所以答案是:不,没有动态代码生成是不可能的。
But with evalit's pretty simple:
但eval它非常简单:
let tpl = eval('`'+a+'`');
回答by Mateusz Moska
In my project I've created something like this with ES6:
在我的项目中,我用 ES6 创建了这样的东西:
String.prototype.interpolate = function(params) {
const names = Object.keys(params);
const vals = Object.values(params);
return new Function(...names, `return \`${this}\`;`)(...vals);
}
const template = 'Example text: ${text}';
const result = template.interpolate({
text: 'Foo Boo'
});
console.log(result);
UPDATEI've removed lodash dependency, ES6 has equivalent methods to get keys and values.
更新我已经删除了 lodash 依赖,ES6 有等效的方法来获取键和值。
回答by Jason Orendorff
What you're asking for here:
你在这里要求的是:
//non working code quoted from the question let b=10; console.log(a.template());//b:10
//non working code quoted from the question let b=10; console.log(a.template());//b:10
is exactly equivalent (in terms of power and, er, safety) to eval: the ability to take a string containing code and execute that code; and also the ability for the executed code to see local variables in the caller's environment.
完全等同于(就功率而言,呃,安全性)eval:获取包含代码的字符串并执行该代码的能力;以及执行代码在调用者环境中查看局部变量的能力。
There is no way in JS for a function to see local variables in its caller, unless that function is eval(). Even Function()can't do it.
在 JS 中,函数无法在其调用者中查看局部变量,除非该函数是eval(). 甚至Function()做不到。
When you hear there's something called "template strings" coming to JavaScript, it's natural to assume it's a built-in template library, like Mustache. It isn't. It's mainly just string interpolationand multiline strings for JS. I think this is going to be a common misconception for a while, though. :(
当您听说 JavaScript 中有一种叫做“模板字符串”的东西时,很自然地会假设它是一个内置的模板库,比如 Mustache。不是。它主要只是用于 JS 的字符串插值和多行字符串。不过,我认为这将是一段时间内的普遍误解。:(
回答by Bryan Rayner
No, there is not a way to do this without dynamic code generation.
不,没有动态代码生成就没有办法做到这一点。
However, I have created a function which will turn a regular string into a function which can be provided with a map of values, using template strings internally.
但是,我创建了一个函数,该函数将在内部使用模板字符串将常规字符串转换为可以提供值映射的函数。
/**
* Produces a function which uses template strings to do simple interpolation from objects.
*
* Usage:
* var makeMeKing = generateTemplateString('${name} is now the king of ${country}!');
*
* console.log(makeMeKing({ name: 'Bryan', country: 'Scotland'}));
* // Logs 'Bryan is now the king of Scotland!'
*/
var generateTemplateString = (function(){
var cache = {};
function generateTemplate(template){
var fn = cache[template];
if (!fn){
// Replace ${expressions} (etc) with ${map.expressions}.
var sanitized = template
.replace(/$\{([\s]*[^;\s\{]+[\s]*)\}/g, function(_, match){
return `$\{map.${match.trim()}\}`;
})
// Afterwards, replace anything that's not ${map.expressions}' (etc) with a blank string.
.replace(/($\{(?!map\.)[^}]+\})/g, '');
fn = Function('map', `return \`${sanitized}\``);
}
return fn;
}
return generateTemplate;
})();
Usage:
用法:
var kingMaker = generateTemplateString('${name} is king!');
console.log(kingMaker({name: 'Bryan'}));
// Logs 'Bryan is king!' to the console.
Hope this helps somebody. If you find a problem with the code, please be so kind as to update the Gist.
希望这可以帮助某人。如果您发现代码有问题,请更新 Gist。
回答by M3D
TLDR: https://jsfiddle.net/w3jx07vt/
TLDR:https://jsfiddle.net/w3jx07vt/
Everyone seems to be worried about accessing variables, why not just pass them? I'm sure it wont be too hard to get the variable context in the caller and pass it down. Use this https://stackoverflow.com/a/6394168/6563504to get the props from obj. I can't test for you right now, but this should work.
每个人似乎都担心访问变量,为什么不直接传递它们呢?我相信在调用者中获取变量上下文并将其传递下去不会太难。使用此https://stackoverflow.com/a/6394168/6563504从 obj 获取道具。我现在无法为您测试,但这应该有效。
function renderString(str,obj){
return str.replace(/$\{(.+?)\}/g,(match,p1)=>{return index(obj,p1)})
}
Tested. Here is full code.
测试。这是完整的代码。
function index(obj,is,value) {
if (typeof is == 'string')
is=is.split('.');
if (is.length==1 && value!==undefined)
return obj[is[0]] = value;
else if (is.length==0)
return obj;
else
return index(obj[is[0]],is.slice(1), value);
}
function renderString(str,obj){
return str.replace(/$\{.+?\}/g,(match)=>{return index(obj,match)})
}
renderString('abc${a}asdas',{a:23,b:44}) //abc23asdas
renderString('abc${a.c}asdas',{a:{c:22,d:55},b:44}) //abc22asdas
回答by didinko
The issue here is to have a function that has access to the variables of its caller. This is why we see direct evalbeing used for template processing. A possible solution would be to generate a function taking formal parameters named by a dictionary's properties, and calling it with the corresponding values in the same order. An alternative way would be to have something simple as this:
这里的问题是有一个可以访问其调用者变量的函数。这就是我们看到直接eval用于模板处理的原因。一种可能的解决方案是生成一个函数,该函数采用由字典属性命名的形式参数,并以相同的顺序使用相应的值调用它。另一种方法是做一些简单的事情:
var name = "John Smith";
var message = "Hello, my name is ${name}";
console.log(new Function('return `' + message + '`;')());
And for anyone using Babel compiler we need to create closure which remembers the environment in which it was created:
对于任何使用 Babel 编译器的人,我们需要创建闭包来记住创建它的环境:
console.log(new Function('name', 'return `' + message + '`;')(name));
回答by pekaaw
There are many good solutions posted here, but none yet which utilizes the ES6 String.raw method. Here is my contriubution. It has an important limitation in that it will only accept properties from a passed in object, meaning no code execution in the template will work.
这里发布了许多很好的解决方案,但还没有一个使用ES6 String.raw 方法。这是我的贡献。它有一个重要的限制,它只接受来自传入对象的属性,这意味着模板中的任何代码都不会执行。
function parseStringTemplate(str, obj) {
let parts = str.split(/$\{(?!\d)[\w??????]*\}/);
let args = str.match(/[^{\}]+(?=})/g) || [];
let parameters = args.map(argument => obj[argument] || (obj[argument] === undefined ? "" : obj[argument]));
return String.raw({ raw: parts }, ...parameters);
}
let template = "Hello, ${name}! Are you ${age} years old?";
let values = { name: "John Doe", age: 18 };
parseStringTemplate(template, values);
// output: Hello, John Doe! Are you 18 years old?
- Split string into non-argument textual parts. See regex.
parts: ["Hello, ", "! Are you ", " years old?"] - Split string into property names. Empty array if match fails.
args: ["name", "age"] - Map parameters from
objby property name. Solution is limited by shallow one level mapping. Undefined values are substituted with an empty string, but other falsy values are accepted.parameters: ["John Doe", 18] - Utilize
String.raw(...)and return result.
- 将字符串拆分为非参数文本部分。请参阅正则表达式。
parts: ["Hello, ", "! Are you ", " years old?"] - 将字符串拆分为属性名称。如果匹配失败,则为空数组。
args: ["name", "age"] obj按属性名称映射参数。解决方案受浅一级映射的限制。未定义的值被替换为空字符串,但也接受其他虚假值。parameters: ["John Doe", 18]- 利用
String.raw(...)并返回结果。
回答by Matt Browne
Similar to Daniel's answer (and s.meijer's gist) but more readable:
类似于 Daniel 的回答(和 s.meijer 的gist),但更具可读性:
const regex = /${[^{]+}/g;
export default function interpolate(template, variables, fallback) {
return template.replace(regex, (match) => {
const path = match.slice(2, -1).trim();
return getObjPath(path, variables, fallback);
});
}
//get the specified property or nested property of an object
function getObjPath(path, obj, fallback = '') {
return path.split('.').reduce((res, key) => res[key] || fallback, obj);
}
Note: This slightly improves s.meijer's original, since it won't match things like ${foo{bar}(the regex only allows non-curly brace characters inside ${and }).
注意:这稍微改进了 s.meijer 的原始版本,因为它不会匹配类似${foo{bar}的东西(正则表达式只允许在${and 中使用非大括号字符})。
UPDATE: I was asked for an example using this, so here you go:
更新:我被要求提供一个使用这个的例子,所以你去:
const replacements = {
name: 'Bob',
age: 37
}
interpolate('My name is ${name}, and I am ${age}.', replacements)
回答by sarkiroka
You can use the string prototype, for example
例如,您可以使用字符串原型
String.prototype.toTemplate=function(){
return eval('`'+this+'`');
}
//...
var a="b:${b}";
var b=10;
console.log(a.toTemplate());//b:10
But the answer of the original question is no way.
但是原问题的答案是没有办法的。
回答by s.meijer
I required this method with support for Internet Explorer. It turned out the back ticks aren't supported by even IE11. Also; using evalor it's equivalent Functiondoesn't feel right.
我需要这种方法支持 Internet Explorer。事实证明,即使是 IE11 也不支持后记号。还; 使用eval或等效的Function感觉不对。
For the one that notice; I also use backticks, but these ones are removed by compilers like babel. The methods suggested by other ones, depend on them on run-time. As said before; this is an issue in IE11 and lower.
对于那个注意到的人;我也使用反引号,但这些反引号已被 babel 等编译器删除。其他人建议的方法在运行时依赖于它们。如前所述; 这是 IE11 及更低版本的问题。
So this is what I came up with:
所以这就是我想出的:
function get(path, obj, fb = `$\{${path}}`) {
return path.split('.').reduce((res, key) => res[key] || fb, obj);
}
function parseTpl(template, map, fallback) {
return template.replace(/$\{.+?}/g, (match) => {
const path = match.substr(2, match.length - 3).trim();
return get(path, map, fallback);
});
}
Example output:
示例输出:
const data = { person: { name: 'John', age: 18 } };
parseTpl('Hi ${person.name} (${person.age})', data);
// output: Hi John (18)
parseTpl('Hello ${person.name} from ${person.city}', data);
// output: Hello John from ${person.city}
parseTpl('Hello ${person.name} from ${person.city}', data, '-');
// output: Hello John from -

