推迟执行ES6模板文字

Cod*_*gue 51 javascript ecmascript-6 template-literals

我正在使用新的ES6模板文字功能,我头脑中的第一件事是String.format用于Javascript,所以我开始实现原型:

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"));
Run Code Online (Sandbox Code Playgroud)

ES6Fiddle

但是,模板文字传递给我的原型方法之前会被评估.有没有什么办法可以编写上面的代码来推迟结果,直到我动态创建元素?

Ber*_*rgi 60

我可以看到三种解决方法:

  • 使用类似于设计使用的模板字符串,没有任何format功能:

    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}`);
    // or even function parameters for actual deferral of the evaluation:
    const welcome = (p0, p1) => `Hello, ${p0}. This is a ${p1}`;
    console.log(welcome("world", "test"));
    
    Run Code Online (Sandbox Code Playgroud)
  • 不要使用模板字符串,而是使用普通的字符串文字:

    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"));
    
    Run Code Online (Sandbox Code Playgroud)
  • 使用标记的模板文字.请注意,仍然会在没有处理程序拦截的情况下评估替换,因此如果没有p0名为so的变量,则不能使用标识符.如果接受不同的替换正文语法提议,则此行为可能会更改(更新:它不是).

    function formatter(literals, ...substitutions) {
        return {
            format: function() {
                var out = [];
                for(var i=0, k=0; i < literals.length; i++) {
                    out[k++] = literals[i];
                    out[k++] = arguments[substitutions[i]];
                }
                out[k] = literals[i];
                return out.join("");
            }
        };
    }
    console.log(formatter`Hello, ${0}. This is a ${1}`.format("world", "test"));
    // Notice the number literals: ^               ^
    
    Run Code Online (Sandbox Code Playgroud)


Rod*_*ues 5

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

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

function fmt([fisrt, ...rest], ...tags) {
  return values => rest.reduce((acc, curr, i) => {
    return acc + values[tags[i]] + curr;
  }, fisrt);
}
Run Code Online (Sandbox Code Playgroud)

然后构建模板并推迟替换:

> 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'
Run Code Online (Sandbox Code Playgroud)

另一种更接近您所写内容的选择是返回从字符串扩展的对象,以开箱即用并尊重界面。对 的扩展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;
}
Run Code Online (Sandbox Code Playgroud)

然后,在呼叫站点中:

> 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.
Run Code Online (Sandbox Code Playgroud)

您可以在这个其他答案中参考更多信息和应用程序。