是否可以创建一个模板字符串作为一个普通的字符串,

let a = "b:${b}";

然后把它转换成一个模板字符串,

let b = 10;
console.log(a.template()); // b:10

没有eval, new Function和其他动态代码生成的手段?


当前回答

你应该试试这个小JS模块,由Andrea Giammarchi,来自github: https://github.com/WebReflection/backtick-template

/*! (C) 2017 Andrea Giammarchi - MIT Style License */
function template(fn, $str, $object) {'use strict';
  var
    stringify = JSON.stringify,
    hasTransformer = typeof fn === 'function',
    str = hasTransformer ? $str : fn,
    object = hasTransformer ? $object : $str,
    i = 0, length = str.length,
    strings = i < length ? [] : ['""'],
    values = hasTransformer ? [] : strings,
    open, close, counter
  ;
  while (i < length) {
    open = str.indexOf('${', i);
    if (-1 < open) {
      strings.push(stringify(str.slice(i, open)));
      open += 2;
      close = open;
      counter = 1;
      while (close < length) {
        switch (str.charAt(close++)) {
          case '}': counter -= 1; break;
          case '{': counter += 1; break;
        }
        if (counter < 1) {
          values.push('(' + str.slice(open, close - 1) + ')');
          break;
        }
      }
      i = close;
    } else {
      strings.push(stringify(str.slice(i)));
      i = length;
    }
  }
  if (hasTransformer) {
    str = 'function' + (Math.random() * 1e5 | 0);
    if (strings.length === values.length) strings.push('""');
    strings = [
      str,
      'with(this)return ' + str + '([' + strings + ']' + (
        values.length ? (',' + values.join(',')) : ''
      ) + ')'
    ];
  } else {
    strings = ['with(this)return ' + strings.join('+')];
  }
  return Function.apply(null, strings).apply(
    object,
    hasTransformer ? [fn] : []
  );
}

template.asMethod = function (fn, object) {'use strict';
  return typeof fn === 'function' ?
    template(fn, this, object) :
    template(this, fn);
};

演示(以下所有测试返回true):

const info = 'template';
// just string
`some ${info}` === template('some ${info}', {info});

// passing through a transformer
transform `some ${info}` === template(transform, 'some ${info}', {info});

// using it as String method
String.prototype.template = template.asMethod;

`some ${info}` === 'some ${info}'.template({info});

transform `some ${info}` === 'some ${info}'.template(transform, {info});

其他回答

这个解决方案不需要ES6:

function render(template, opts) {
  return new Function(
    'return new Function (' + Object.keys(opts).reduce((args, arg) => args += '\'' + arg + '\',', '') + '\'return `' + template.replace(/(^|[^\\])'/g, '$1\\\'') + '`;\'' +
    ').apply(null, ' + JSON.stringify(Object.keys(opts).reduce((vals, key) => vals.push(opts[key]) && vals, [])) + ');'
  )();
}

render("hello ${ name }", {name:'mo'}); // "hello mo"

注意:Function构造函数总是在全局作用域中创建,这可能会导致全局变量被模板覆盖,例如render("hello ${someGlobalVar = 'some new value'}", {name:'mo'});

在我的项目中,我用ES6创建了这样的东西:

String.prototype.interpolate =函数(参数){ const names = Object.keys(params); const vals = Object.values(params); 返回新功能(…的名字,‘返回’$ { }\`;`)(... 瓦尔斯); } const template = '示例文本:${text}'; Const result = template.interpolate({ 文本:“Foo Boo” }); console.log(结果);

我提出了这个实现,它的工作就像一个魅力。

function interpolateTemplate(template: string, args: any): string {
  return Object.entries(args).reduce(
    (result, [arg, val]) => result.replace(`$\{${arg}}`, `${val}`),
    template,
  )
}

const template = 'This is an example: ${name}, ${age} ${email}'

console.log(interpolateTemplate(template,{name:'Med', age:'20', email:'example@abc.com'}))

如果在模板中没有找到arg,可能会引发错误

不,如果没有动态代码生成,就没有办法做到这一点。

然而,我已经创建了一个函数,它将把一个常规字符串转换为一个函数,可以提供一个值的映射,在内部使用模板字符串。

生成模板字符串

/**
 * 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;
})();

用法:

var kingMaker = generateTemplateString('${name} is king!');

console.log(kingMaker({name: 'Bryan'}));
// Logs 'Bryan is king!' to the console.

希望这能帮助到一些人。如果你发现代码有问题,请及时更新Gist。

我目前不能评论现有的答案,所以我无法直接评论布莱恩·雷诺的出色回答。因此,这个回答将会更新他的答案,稍微修正一下。

简而言之,他的函数实际上没有缓存创建的函数,所以它总是重新创建,不管之前是否见过模板。以下是修正后的代码:

    /**
     * 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 = cache[template] = Function('map', `return \`${sanitized}\``);
            }

            return fn;
        };

        return generateTemplate;
    })();