Javascript 推迟ES6模板文本的执行

Javascript 推迟ES6模板文本的执行,javascript,ecmascript-6,template-literals,Javascript,Ecmascript 6,Template Literals,我正在使用新功能,我想到的第一件事是JavaScript的String.format,因此我开始实现一个原型: String.prototype.format = function() { var self = this; arguments.forEach(function(val,idx) { self["p"+idx] = val; }); return this.toString(); }; console.log(`Hello, ${p0}. This is a

我正在使用新功能,我想到的第一件事是JavaScript的
String.format
,因此我开始实现一个原型:

String.prototype.format = function() {
  var self = this;
  arguments.forEach(function(val,idx) {
    self["p"+idx] = val;
  });
  return this.toString();
};
console.log(`Hello, ${p0}. This is a ${p1}`.format("world", "test"));


但是,在将模板文本传递给原型方法之前,会对其进行评估。是否有任何方法可以编写上述代码,将结果延迟到动态创建元素之后?

我可以看到三种方法:

  • 使用模板字符串,就像它们被设计用来使用一样,不使用任何
    格式
    函数:

    console.log(`Hello, ${"world"}. This is a ${"test"}`);
    // might make more sense with variables:
    var p0 = "world", p1 = "test";
    console.log(`Hello, ${p0}. This is a ${p1}`);
    
    甚至功能参数用于实际延迟评估:

    const welcome = (p0, p1) => `Hello, ${p0}. This is a ${p1}`;
    console.log(welcome("world", "test"));
    
  • 不要使用模板字符串,而是使用普通字符串文字:

    String.prototype.format = function() {
        var args = arguments;
        return this.replace(/\$\{p(\d)\}/g, function(match, id) {
            return args[id];
        });
    };
    console.log("Hello, ${p0}. This is a ${p1}".format("world", "test"));
    
  • 使用标记的模板文本。请注意,替换仍将在不被处理程序拦截的情况下进行计算,因此如果没有名为so的变量,则不能使用
    p0
    之类的标识符。如果(更新:未更新),此行为可能会更改

    函数格式化程序(文本,…替换){
    返回{
    格式:函数(){
    var out=[];
    for(变量i=0,k=0;i

我发布了一个类似问题的答案,给出了两种延迟执行模板文本的方法。当模板文字位于函数中时,仅在调用函数时才计算模板文字,并使用函数的作用域对其进行计算


我也喜欢
String.format
函数的想法,并且能够显式定义解析变量

这就是我想到的。。。基本上是一个
字符串。用
deepObject
查找替换
方法

const isUndefined=o=>typeof o=='undefined'
常量nvl=(o,valueIfUndefined)=>isUndefined(o)?值未定义:o
//给定“路径”,从对象获取深度值。
常量getDeepValue=(对象,路径)=>
路径
.替换(/\[\]\.?/g,“.”)
.split(“.”)
.filter(s=>s)
.减少((acc,val)=>acc和acc[val],obj)
//给定一个字符串,解析所有模板变量。
const resolveTemplate=(str,变量)=>{
返回str.replace(/\$\{([^\}]+)\}/g,(m,g1)=>
nvl(getDeepValue(变量,g1),m))
}
//向字符串原型添加“格式”方法。
String.prototype.format=函数(变量){
返回resolveTemplate(此,变量)
}
//设置解析变量。。。
变量变量={}
变量['top level']='Foo'
变量['deep object']={text:'Bar'}
变量aGlobalVariable='Dog'

//==>Foo Bar Dog Dog${不是一个对象。text}扩展@Bergi的答案,当您意识到可以返回任何结果时,带标签的模板字符串的力量就会显现出来,而不仅仅是普通字符串。在他的示例中,标记构造并返回一个具有闭包和函数属性
format
的对象

在我最喜欢的方法中,我自己返回一个函数值,您可以稍后调用它并传递新参数来填充模板。像这样:

function fmt([fisrt, ...rest], ...tags) {
  return values => rest.reduce((acc, curr, i) => {
    return acc + values[tags[i]] + curr;
  }, fisrt);
}
然后构建模板并推迟替换:

> fmt`Test with ${0}, ${1}, ${2} and ${0} again`(['A', 'B', 'C']);
// 'Test with A, B, C and A again'
> template = fmt`Test with ${'foo'}, ${'bar'}, ${'baz'} and ${'foo'} again`
> template({ foo:'FOO', bar:'BAR' })
// 'Test with FOO, BAR, undefined and FOO again'

另一个更接近您所写内容的选项是,返回一个从字符串扩展而来的对象,让duck直接输入并尊重接口。
String.prototype
的扩展不起作用,因为以后需要关闭模板标记来解析参数

class FormatString extends String {
  // Some other custom extensions that don't need the template closure
}

function fmt([fisrt, ...rest], ...tags) {
  const str = new FormatString(rest.reduce((acc, curr, i) => `${acc}\${${tags[i]}}${curr}`, fisrt));
  str.format = values => rest.reduce((acc, curr, i) => {
    return acc + values[tags[i]] + curr;
  }, fisrt);
  return str;
}
然后,在呼叫站点中:

> console.log(fmt`Hello, ${0}. This is a ${1}.`.format(["world", "test"]));
// Hello, world. This is a test.
> template = fmt`Hello, ${'foo'}. This is a ${'bar'}.`
> console.log(template)
// { [String: 'Hello, ${foo}. This is a ${bar}.'] format: [Function] }
> console.log(template.format({ foo: true, bar: null }))
// Hello, true. This is a null.
您可以参考。

AFAIS中的更多信息和应用程序,有用的功能“字符串模板的延迟执行”仍然不可用。使用lambda是一种表达性、可读性和简短的解决方案,但是:

var greetingTmpl = (...p)=>`Hello, ${p[0]}. This is a ${p[1]}`;

console.log( greetingTmpl("world","test") );
console.log( greetingTmpl("@CodingIntrigue","try") );

您可以使用下面的函数将值注入字符串

let inject = (str, obj) => str.replace(/\${(.*?)}/g, (x,g)=> obj[g]);
let inject=(str,obj)=>str.replace(/\${(.*?})/g,(x,g)=>obj[g]);
//---测试---
//对象中的参数
让t1='我的名字是${name},我是${age}。我兄弟的名字也是${name};
设r1=注入(t1,{姓名:'JOHN',年龄:23});
log(“对象:”,r1);
//数组中的参数
设t2=“值${0}位于${2}数组中,${1}值为${0}。”
设r2=注入(t2,{…['A,B,C',666,'BIG']});

log(“数组:”,r2)虽然这个问题已经得到了回答,但这里有一个加载配置文件时使用的简单实现(代码是typescript,但很容易转换为JS,只需删除键入):

/**
*这种方法有许多局限性:
*-不接受带有数字或其他符号的变量名(相对容易修复)
*-它不接受任意表达式(很难修复)
*/
函数deferredTemplateLiteral(模板:string,环境:{[key:string]:string | undefined}):string{
常数varsMatcher=/\${([a-zA-Z\]+)}/
常量globalVarsmatcher=/\${[a-zA-Z\]+}/g
const varMatches:string[]=template.match(globalVarsmatcher)??[]
const templateVarNames=varMatches.map(v=>v.match(varsMatcher)?[1]??“”
const templateValues:(字符串|未定义)[]=templateVarNames.map(v=>env[v])
const templateInterpolator=新函数(…[…templateVarNames,`return\`${template}\`;`])
返回templateInterpolator(…templateValues)
}
//用法:
延迟模板文件(“hello${thing},{thing:“world”})==“hello world”
尽管有可能使这些东西更强大、更灵活,但它带来了太多的复杂性和风险,却没有带来太多好处

这里有一个指向要点的链接:

(参见上面@Bergi非常类似的回答)

函数插值(字符串,…位置){
var错误=位置。
let inject = (str, obj) => str.replace(/\${(.*?)}/g, (x,g)=> obj[g]);