【问题标题】:How can I do string interpolation in JavaScript?如何在 JavaScript 中进行字符串插值?
【发布时间】:2021-09-28 17:40:05
【问题描述】:

考虑这段代码:

var age = 3;

console.log("I'm " + age + " years old!");

除了字符串连接之外,还有其他方法可以将变量的值插入字符串吗?

【问题讨论】:

  • 您可以查看 CoffeeScript:coffeescript.org
  • 正如其他人所指出的,您使用的方法最简单的方法。我希望能够引用字符串中的变量,但在 javascript 中,您需要使用连接(或其他一些查找/替换方法)。像你我这样的人可能只是为了我们自己的利益而过于依赖 PHP。
  • 使用 Underscore.js template
  • 连接不是插值,用户询问如何进行插值。我认为 Lev 最终提供了答案,即在原生 JS 中没有办法做到这一点。我不明白为什么这不是一个真正的问题。
  • 如果我可以回答,现在有一个针对较新解释器的解决方案(2015 FF 和 Chrome 已经支持):developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…。例如。 Show GRAPH of : ${fundCode}-${funcCode} 让我Show GRAPH of : AIP001-_sma (在字符串 1 周围使用反引号......似乎无法在此处显示)

标签: javascript string string-interpolation


【解决方案1】:

从 ES6 开始,你可以使用template literals:

const age = 3
console.log(`I'm ${age} years old!`)

P.S.注意使用反引号:``

【讨论】:

  • 我想知道为什么他们会在这上面加上反引号,难道“${age}”不足以进行插值吗?
  • @LaughingLemonade 向后兼容性。如果以这种方式实现,所有以该格式打印字符串的现有代码都将失败。
  • @Jonathan 我总是不喜欢反引号作为一个有意义的字符。但我不确定原因是否普遍或仅适用于我的语言键盘布局。 Backtick 是一种特殊的字符,它会等待被写入直到下一个字符,要求它被击中两次才能写入(并且此时写入其中两个)。这是因为一些字符与它们交互,例如“e”。如果我尝试和他们一起写“hello”,我会得到 èllo``。它的位置也有些烦人(shift+forwardtick,这是另一个特殊的前瞻字符),因为它需要转换到类型,不像'.
  • @felix 这是一个特定于您的键盘布局的想法;您所描述的称为“死键”。它们也出现在挪威键盘布局(我来自哪里)中,这也是我为所有编程切换到美国键盘布局的部分原因。 (还有许多其他字符 - 例如 [ 和 ] - 在美式键盘上也更容易使用。)
【解决方案2】:

tl;博士

如果适用,请使用 ECMAScript 2015 的模板字符串文字。

说明

根据 ECMAScript 5 规范,没有直接的方法可以做到这一点,但 ECMAScript 6 有template strings,在起草规范时也称为quasi-literals。像这样使用它们:

> var n = 42;
undefined
> `foo${n}bar`
'foo42bar'

您可以在{} 中使用任何有效的JavaScript 表达式。例如:

> `foo${{name: 'Google'}.name}bar`
'fooGooglebar'
> `foo${1 + 3}bar`
'foo4bar'

另一件重要的事情是,您不必再担心多行字符串了。你可以把它们简单地写成

> `foo
...     bar`
'foo\n    bar'

注意:我使用 io.js v2.4.0 来评估上面显示的所有模板字符串。您还可以使用最新的 Chrome 来测试上面显示的示例。

注意: ES6 规范为now finalized,但尚未被所有主要浏览器实现。
根据Mozilla Developer Network pages,这将从以下版本开始实现基本支持:Firefox 34、Chrome 41、Internet Explorer 12。如果您是 Opera、Safari 或 Internet Explorer 用户并且对以下版本感到好奇这个现在,this test bed 可以用来玩,直到每个人都得到支持。

【讨论】:

  • 如果你使用babeljs,它是可用的,所以你可以在你的代码库中引入它,然后一旦你需要支持的浏览器实现它,就放弃编译步骤。
  • 有没有办法将标准字符串转换为模板字符串文字?例如,如果一个 json 文件包含一个转换表,该转换表需要插入其中的值以用于显示目的?我认为第一个解决方案可能适用于这种情况,但我确实喜欢新的字符串模板语法。
  • 由于这仍然是 js 字符串插值的首批搜索结果之一,如果您现在可以更新它以反映普遍可用性,那就太好了。对于大多数浏览器来说,“没有直接的方法”可能不再适用。
  • 对于视力不好的观众来说,`character is different than '。如果您无法使其正常工作,请确保您使用严重引用,(又名倾斜引用,反向引用)en.wikipedia.org/wiki/Grave_accent。它位于键盘的左上角 :)
  • @Quincy Mac 键盘的左下角 - 也称为反引号 :)
【解决方案3】:

Douglas Crockford 的 Remedial JavaScript 包含一个 String.prototype.supplant 函数。它简短、熟悉且易于使用:

String.prototype.supplant = function (o) {
    return this.replace(/{([^{}]*)}/g,
        function (a, b) {
            var r = o[b];
            return typeof r === 'string' || typeof r === 'number' ? r : a;
        }
    );
};

// Usage:
alert("I'm {age} years old!".supplant({ age: 29 }));
alert("The {a} says {n}, {n}, {n}!".supplant({ a: 'cow', n: 'moo' }));

如果您不想更改 String 的原型,您可以随时将其调整为独立的,或将其放入其他命名空间,或其他任何方式。

【讨论】:

  • 注意:这将比仅仅连接慢十倍。
  • 并且需要大约三倍的击键次数和字节数。
  • @roosteronacid - 你能对速度下降给出一些看法吗?比如,从 0.01s 到 0.1s(重要)或从 0.000000001s 到 0.00000001s(不相关)?
  • @george:在我的机器上进行的快速测试给出了 7312 ns 的“The cow says moo, moo, moo!”将 Crockford 的方法与 111 ns 用于预编译函数,该函数将变量从传递的对象中提取出来,并将它们与模板字符串的常量部分连接起来。这是 Chrome 21。
  • 或者像这样使用它:"The {0} says {1}, {1}, {1}!".supplant(['cow', 'moo'])
【解决方案4】:

注意事项:避免使用任何不允许您转义自己的分隔符的模板系统。例如,使用这里提到的supplant() 方法将无法输出以下内容。

“感谢我的 {age} 变量,我已经 3 岁了。”

简单的插值可能适用于小型独立脚本,但通常会出现这种设计缺陷,这将限制任何严重的使用。老实说,我更喜欢 DOM 模板,例如:

<div> I am <span id="age"></span> years old!</div>

并使用 jQuery 操作:$('#age').text(3)

另外,如果您只是厌倦了字符串连接,总有替代语法:

var age = 3;
var str = ["I'm only", age, "years old"].join(" ");

【讨论】:

  • 旁注:Array.join() 比直接(+ 风格)连接要慢,因为浏览器引擎(包括 V8,包括 node 和几乎所有今天运行 JS 的东西)已经对其进行了大规模优化,并且direct concatenation 有很大的不同
  • 替代方法确实允许您生成您提到的字符串:{token} 只有在数据对象包含名为 token 的成员时才会被替换 - 因此前提是您不提供数据对象有一个age 成员,就可以了。
  • 克里斯,我不明白这是一个解决方案。我可以轻松地更新示例以同时使用年龄变量和 {age} 字符串。你真的想担心你可以根据模板复制文本命名你的变量吗? --此外,自从这篇文章以来,我已经成为数据绑定库的忠实粉丝。一些,比如 RactiveJS,会让你从一个充满可变跨度的 DOM 中解脱出来。与 Mustache 不同的是,它只更新页面的那一部分。
  • 您的主要答案似乎假设此 javascript 在浏览器环境中运行,即使该问题未使用 HTML 标记。
  • 您对supplant 的“警告”是没有根据的:"I am 3 years old thanks to my {age} variable.".supplant({})); 准确返回给定的字符串。如果给定age,仍然可以使用{{age}} 打印{}
【解决方案5】:

当我还不知道如何正确地做到这一点时,我会在很多语言中使用这种模式,只是想快速获得一个想法:

// JavaScript
let stringValue = 'Hello, my name is {name}. You {action} my {relation}.'
    .replace(/{name}/g    ,'Inigo Montoya')
    .replace(/{action}/g  ,'killed')
    .replace(/{relation}/g,'father')
    ;

虽然不是特别有效,但我觉得它可读。它始终有效,并且始终可用:

' VBScript
dim template = "Hello, my name is {name}. You {action} my {relation}."
dim stringvalue = template
stringValue = replace(stringvalue, "{name}"    ,"Luke Skywalker")     
stringValue = replace(stringvalue, "{relation}","Father")     
stringValue = replace(stringvalue, "{action}"  ,"are")

总是

* COBOL
INSPECT stringvalue REPLACING FIRST '{name}'     BY 'Grendel'
INSPECT stringvalue REPLACING FIRST '{relation}' BY 'Mother'
INSPECT stringvalue REPLACING FIRST '{action}'   BY 'did unspeakable things to'

【讨论】:

  • IE 不支持模板文字,因此当您不希望需要单独的包(例如 sprintf)时,这是一个不错的解决方案。比连接字符串更简洁,尤其是在处理具有动态属性和内容的 html 标记时。
【解决方案6】:

如果你真的想用大锤敲碎坚果,你可以使用Prototype's template system

var template = new Template("I'm #{age} years old!");
alert(template.evaluate({age: 21}));

【讨论】:

【解决方案7】:

试试sprintf 库(一个完整的开源 JavaScript sprintf 实现)。例如:

vsprintf('The first 4 letters of the english alphabet are: %s, %s, %s and %s', ['a', 'b', 'c', 'd']);

vsprintf 接受一个参数数组并返回一个格式化字符串。

【讨论】:

    【解决方案8】:

    如果您想在console.log 输出中进行插值,那么只需

    console.log("Eruption 1: %s", eruption1);
                             ^^
    

    这里,%s 是所谓的“格式说明符”。 console.log 内置了这种插值支持。

    【讨论】:

      【解决方案9】:

      您可以使用 ES6 template string 轻松完成并使用任何可用的转译器(如 babel)转译为 ES5。

      const age = 3;
      
      console.log(`I'm ${age} years old!`);
      

      http://www.es6fiddle.net/im3c3euc/

      【讨论】:

        【解决方案10】:

        试试kiwi,一个用于字符串插值的轻量级 JavaScript 模块。

        你可以的

        Kiwi.compose("I'm % years old!", [age]);
        

        Kiwi.compose("I'm %{age} years old!", {"age" : age});
        

        【讨论】:

          【解决方案11】:

          这是一个解决方案,它要求您为对象提供值。如果您不提供对象作为参数,它将默认使用全局变量。但最好还是坚持使用参数,这样更简洁。

          String.prototype.interpolate = function(props) {
              return this.replace(/\{(\w+)\}/g, function(match, expr) {
                  return (props || window)[expr];
              });
          };
          
          // Test:
          
          // Using the parameter (advised approach)
          document.getElementById("resultA").innerText = "Eruption 1: {eruption1}".interpolate({ eruption1: 112 });
          
          // Using the global scope
          var eruption2 = 116;
          document.getElementById("resultB").innerText = "Eruption 2: {eruption2}".interpolate();
          &lt;div id="resultA"&gt;&lt;/div&gt;&lt;div id="resultB"&gt;&lt;/div&gt;

          【讨论】:

          • 不要使用evaleval 是邪恶的!
          • @chris97ong 虽然这是“正确的”,但至少给出一个理由(“邪恶”没有帮助)或替代解决方案。几乎总有办法使用eval,但有时不是。例如,如果 OP 想要一种使用当前范围进行插值的方法,而不必传递查找对象(例如 Groovy 插值的工作方式),我很确定 eval 将是必需的。不要只诉诸旧的“评估是邪恶的”。
          • 从不使用 eval 也不建议使用它
          • @hasenj 这就是为什么我说它“可能不是最好的主意” 并提供了一种替代方法。但不幸的是,eval 是访问作用域内局部变量的唯一方法。所以不要仅仅因为它伤害了你的感情而拒绝它。顺便说一句,我也更喜欢替代方法,因为它更安全,但 eval 方法是 精确 回答 OP 的问题,因此它在答案中。
          • eval 的问题是无法从另一个作用域访问 vars,所以如果你的 .interpolate 调用在另一个函数中,而不是全局的,它就不会工作。
          【解决方案12】:

          最简单的应该是

          `my string ${VARIABLE}`
          

          实现这一目标的效率较低的方法是

          function format(str, ...params) {
            for(const param of params)
              str = str.replace("%", param);
             return str;
          }
          

          可以搭配使用

          format("My % string", "interpolation")
          

          【讨论】:

            【解决方案13】:

            扩展Greg Kindel's第二个答案,您可以编写一个函数来消除一些样板:

            var fmt = {
                join: function() {
                    return Array.prototype.slice.call(arguments).join(' ');
                },
                log: function() {
                    console.log(this.join(...arguments));
                }
            }
            

            用法:

            var age = 7;
            var years = 5;
            var sentence = fmt.join('I am now', age, 'years old!');
            fmt.log('In', years, 'years I will be', age + years, 'years old!');
            

            【讨论】:

              【解决方案14】:

              我可以举个例子:

              function fullName(first, last) {
                let fullName = first + " " + last;
                return fullName;
              }
              
              function fullNameStringInterpolation(first, last) {
                let fullName = `${first} ${last}`;
                return fullName;
              }
              
              console.log('Old School: ' + fullName('Carlos', 'Gutierrez'));
              
              console.log('New School: ' + fullNameStringInterpolation('Carlos', 'Gutierrez'));

              【讨论】:

                【解决方案15】:

                找不到我要找的东西,然后找到了 -

                如果您使用的是 Node.js,则有一个内置的 utilpackage 具有格式功能,其工作方式如下:

                util.format("Hello my name is %s", "Brent");
                > Hello my name is Brent
                

                巧合的是,这现在也在 Node.js 中内置到 console.log 风格中 -

                console.log("This really bad error happened: %s", "ReferenceError");
                > This really bad error happened: ReferenceError
                

                【讨论】:

                  【解决方案16】:
                  let age = 3;
                  
                  console.log(`I'm ${age} years old!`);
                  

                  您可以使用 反引号 ``ES6 模板字符串

                  【讨论】:

                    【解决方案17】:

                    从 ES6 开始,如果你想在对象键中进行字符串插值,你会得到一个SyntaxError: expected property name, got '${',如果你这样做:

                    let age = 3
                    let obj = { `${age}`: 3 }
                    

                    您应该改为:

                    let obj = { [`${age}`]: 3 }
                    

                    【讨论】:

                      【解决方案18】:

                      为@Chris Nielsen 帖子的ES6 版本替换更多内容。

                      String.prototype.supplant = function (o) {
                        return this.replace(/\${([^\${}]*)}/g,
                          (a, b) => {
                            var r = o[b];
                            return typeof r === 'string' || typeof r === 'number' ? r : a;
                          }
                        );
                      };
                      
                      string = "How now ${color} cow? {${greeting}}, ${greeting}, moo says the ${color} cow.";
                      
                      string.supplant({color: "brown", greeting: "moo"});
                      => "How now brown cow? {moo}, moo, moo says the brown cow."
                      

                      【讨论】:

                        【解决方案19】:

                        在旧浏览器中使用模板语法会失败,这对于创建供公众使用的 HTML 很重要。使用连接是乏味且难以阅读的,特别是如果您有很多或很长的表达式,或者如果您必须使用括号来处理数字和字符串项目的混合(两者都使用 + 运算符)。

                        PHP 使用非常紧凑的符号扩展包含变量甚至某些表达式的引用字符串:$a="the color is $color";

                        在 JavaScript 中,可以编写一个高效的函数来支持这一点:var a=S('the color is ',color);,使用可变数量的参数。虽然在这个例子中并没有优于串联,但当表达式变长时,这种语法可能会更清晰。或者可以使用美元符号来表示使用 JavaScript 函数的表达式的开始,就像在 PHP 中一样。

                        另一方面,编写一个有效的解决方法来为旧版浏览器提供类似模板的字符串扩展并不难。可能已经有人这样做了。

                        最后,我认为 sprintf(如在 C、C++ 和 PHP 中)可以用 JavaScript 编写,尽管它的效率会比其他这些解决方案低一些。

                        【讨论】:

                          【解决方案20】:

                          自定义灵活插值:

                          var sourceElm = document.querySelector('input')
                          
                          // interpolation callback
                          const onInterpolate = s => `<mark>${s}</mark>`
                          
                          // listen to "input" event
                          sourceElm.addEventListener('input', parseInput) 
                          
                          // parse on window load
                          parseInput() 
                          
                          // input element parser
                          function parseInput(){
                            var html = interpolate(sourceElm.value, undefined, onInterpolate)
                            sourceElm.nextElementSibling.innerHTML = html;
                          }
                          
                          // the actual interpolation 
                          function interpolate(str, interpolator = ["{{", "}}"], cb){
                            // split by "start" pattern
                            return str.split(interpolator[0]).map((s1, i) => {
                              // first item can be safely ignored
                          	  if( i == 0 ) return s1;
                              // for each splited part, split again by "end" pattern 
                              const s2 = s1.split(interpolator[1]);
                          
                              // is there's no "closing" match to this part, rebuild it
                              if( s1 == s2[0]) return interpolator[0] + s2[0]
                              // if this split's result as multiple items' array, it means the first item is between the patterns
                              if( s2.length > 1 ){
                                  s2[0] = s2[0] 
                                    ? cb(s2[0]) // replace the array item with whatever
                                    : interpolator.join('') // nothing was between the interpolation pattern
                              }
                          
                              return s2.join('') // merge splited array (part2)
                            }).join('') // merge everything 
                          }
                          input{ 
                            padding:5px; 
                            width: 100%; 
                            box-sizing: border-box;
                            margin-bottom: 20px;
                          }
                          
                          *{
                            font: 14px Arial;
                            padding:5px;
                          }
                          <input value="Everything between {{}} is {{processed}}" />
                          <div></div>

                          【讨论】:

                            【解决方案21】:

                            虽然模板可能最适合您描述的情况,但如果您拥有或想要可迭代/数组形式的数据和/或参数,您可以使用String.raw

                            String.raw({
                              raw: ["I'm ", " years old!"]
                            }, 3);
                            

                            将数据作为数组,可以使用扩展运算符:

                            const args = [3, 'yesterday'];
                            String.raw({
                              raw: ["I'm ", " years old as of ", ""]
                            }, ...args);
                            

                            【讨论】:

                              猜你喜欢
                              相关资源
                              最近更新 更多