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

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

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

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

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


当前回答

这里的问题是有一个函数可以访问它的调用者的变量。这就是为什么我们看到直接eval被用于模板处理。一种可能的解决方案是生成一个函数,接受由字典属性命名的形式参数,并以相同的顺序使用相应的值调用它。另一种方法是这样简单:

var name = "John Smith";
var message = "Hello, my name is ${name}";
console.log(new Function('return `' + message + '`;')());

对于任何使用Babel编译器的人,我们需要创建一个闭包,它可以记住创建它的环境:

console.log(new Function('name', 'return `' + message + '`;')(name));

其他回答

@Mateusz Moska,解决方案很好,但当我在React Native(构建模式)中使用它时,它抛出一个错误:无效字符'”,尽管当我在调试模式下运行它时它是有效的。

所以我用正则表达式写出了我自己的解。

String.prototype.interpolate = function(params) {
  let template = this
  for (let key in params) {
    template = template.replace(new RegExp('\\$\\{' + key + '\\}', 'g'), params[key])
  }
  return template
}

const template = 'Example text: ${text}',
  result = template.interpolate({
    text: 'Foo Boo'
  })

console.log(result)

演示:https://es6console.com/j31pqx1p/

注:因为我不知道问题的根本原因,我在react-native repo中提出了一张票,https://github.com/facebook/react-native/issues/14107,这样一旦他们能够修复/指导我大致相同:)

你应该试试这个小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});

您可以参考这个解决方案 Const interpolate = (str) => new Function(' return \ ' ${new String(str)}\ '; ')(); const foo = 'My'; Const obj = { 文本:汉尼拔·莱克特 firstNum: 1、 secondNum: 2 } Const STR = "${foo}名称为:${obj.text}。Sum = ${obj。firstNum} + ${obj。secondNum} = ${obj。firstNum + obj.secondNum}"; console.log(插入(str));

类似于Daniel的回答(以及s.m ijer的要点),但更易于阅读:

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);
}

注意:这稍微改进了s.m ijer的原始版本,因为它不会匹配像${foo{bar}这样的东西(正则表达式只允许${和}内的非花括号字符)。


更新:我被要求使用这个例子,所以你去:

const replacements = {
    name: 'Bob',
    age: 37
}

interpolate('My name is ${name}, and I am ${age}.', replacements)

这里的问题是有一个函数可以访问它的调用者的变量。这就是为什么我们看到直接eval被用于模板处理。一种可能的解决方案是生成一个函数,接受由字典属性命名的形式参数,并以相同的顺序使用相应的值调用它。另一种方法是这样简单:

var name = "John Smith";
var message = "Hello, my name is ${name}";
console.log(new Function('return `' + message + '`;')());

对于任何使用Babel编译器的人,我们需要创建一个闭包,它可以记住创建它的环境:

console.log(new Function('name', 'return `' + message + '`;')(name));