【问题标题】:ES6 template literals vs. concatenated stringsES6 模板文字与串联字符串
【发布时间】:2015-02-18 07:56:33
【问题描述】:

我有以下ECMAScript 6 的代码模板文字

let person = {name: 'John Smith'};
let tpl = `My name is ${person.name}.`;
let MyVar = "My name is " + person.name + ".";

console.log("template literal= " + tpl);
console.log("my variable = " + MyVar);

输出如下:

template literal= My name is John Smith.
my variable = My name is John Smith.

This 是小提琴。

我尝试寻找确切的差异,但找不到, 以下两种说法有什么区别?

  let tpl = `My name is ${person.name}.`;

  let MyVar = "My name is "+ person.name+".";

我已经能够在此处获得与person.name 连接的字符串MyVar,那么使用模板文字的场景是什么?

【问题讨论】:

  • 这是其他语言的共同特点,快到时候了!看起来更干净,而且是多行的。
  • 不确定您所说的“差异”是什么意思,如tpl === MyVar?唯一的区别是创建它们的语法。请注意,与字符串连接相比,模板还提供标记功能,可用于自动转义等功能。
  • 你基本上是在问字符串插值和字符串连接有什么区别。
  • 速度差异不值得考虑 - 如果纳秒是您的瓶颈,那么您需要解决其他更大的问题,例如另一台服务器等。我更喜欢 concat,因为主观上反引号看起来太类似于撇号在其他地方使用。

标签: javascript node.js ecmascript-6 template-strings


【解决方案1】:

虽然我的回答没有直接解决这个问题,但我认为指出使用模板文字来支持数组连接的一个缺点可能会引起一些兴趣。

假设我有

let patient1 = {firstName: "John", lastName: "Smith"};
let patient2 = {firstName: "Dwayne", lastName: "Johnson", middleName: "'The Rock'"};

所以有些患者有中间名,而有些则没有。

如果我想要一个代表患者全名的字符串

let patientName = `${patient1.firstName} ${patient1.middleName} ${patient1.lastName}`;

那么这将变成“John undefined Smith”

如果我这样做了

let patientName = [patient1.firstName, patient1.middleName,  patient1.lastName].join(" ");

那么这将变成“John Smith”

General_Twyckenham 指出“” 上的连接会导致“John”和“Smith”之间出现额外的空格。

要解决这个问题,您可以在连接之前使用过滤器来去除虚假值:

[patient1.firstName, patient1.middleName, patient1.lastName].filter(el => el).join(" ");

【讨论】:

  • 实际上,这并不完全正确 - join 版本会给您 John Smith,并有一个额外的空间。可以想象,这通常是不可取的。解决此问题的方法是使用 map,如下所示:[patient1.firstName, patient1.middleName, patient1.lastName].map(el => el).join(" ");
  • @General_Twyckenham 啊我明白你的意思。接得好。此外,它应该是过滤器而不是映射来摆脱多余的空间。我会编辑我的答案,谢谢。
  • 哎呀 - 是的,filter 是我的意思。
  • 根据这个讨论,字符串连接比数组连接更快。 stackoverflow.com/questions/7299010/…
【解决方案2】:

ES6 提出了一种新类型的字符串文字,使用` 反引号作为分隔符。这些文字确实允许嵌入基本的字符串插值表达式,然后自动解析和评估。

let actor = {name: 'RajiniKanth', age: 68};

let oldWayStr = "<p>My name is " + actor.name + ",</p>\n" +
  "<p>I am " + actor.age + " old</p>\n";

let newWayHtmlStr =
 `<p>My name is ${actor.name},</p>
  <p>I am ${actor.age} old</p>`;

console.log(oldWayStr);
console.log(newWayHtmlStr);

如您所见,我们在一系列字符周围使用了 ..``,这些字符被解释为字符串文字,但 ${..} 形式的任何表达式都会被立即解析并内联计算。

内插字符串字面量的一个非常好的好处是它们可以分成多行:

var Actor = {"name" : "RajiniKanth"};

var text =
`Now is the time for all good men like ${Actor.name}
to come to the aid of their
country!`;
console.log( text );
// Now is the time for all good men
// to come to the aid of their
// country!

插值表达式

任何有效的表达式都可以出现在${..} 内插值字符串lit‐ eral 中,包括函数调用、内联函数表达式调用,甚至是其他interpo‐ lated string literals

function upper(s) {
  return s.toUpperCase();
}
var who = "reader"
var text =
`A very ${upper( "warm" )} welcome
to all of you ${upper( `${who}s` )}!`;
console.log( text );
// A very WARM welcome
// to all of you READERS!

在这里,当将 who 变量与 "s" 字符串结合起来时,内部 ${who}s`` 内插字符串文字对我们来说更方便一些,而不是 who + "s"。另外要注意的是,插入的字符串文字只是 lexically scoped 它出现的位置,而不是 dynamically scoped 以任何方式:

function foo(str) {
  var name = "foo";
  console.log( str );
}
function bar() {
  var name = "bar";
  foo( `Hello from ${name}!` );
}
var name = "global";
bar(); // "Hello from bar!"

在 HTML 中使用template literal 通过减少烦恼肯定更具可读性。

简单的老方法:

'<div class="' + className + '">' +
  '<p>' + content + '</p>' +
  '<a href="' + link + '">Let\'s go</a>'
'</div>';

使用 ES6:

`<div class="${className}">
  <p>${content}</p>
  <a href="${link}">Let's go</a>
</div>`
  • 您的字符串可以跨越多行。
  • 您不必转义引号字符。
  • 您可以避免像这样的分组:'">'
  • 您不必使用加号运算符。

标记模板文字

我们还可以标记template 字符串,当标记template 字符串时,literals 和替换被传递给返回结果值的函数。

function myTaggedLiteral(strings) {
  console.log(strings);
}

myTaggedLiteral`test`; //["test"]

function myTaggedLiteral(strings,value,value2) {
  console.log(strings,value, value2);
}
let someText = 'Neat';
myTaggedLiteral`test ${someText} ${2 + 3}`;
// ["test ", " ", ""]
// "Neat"
// 5

我们可以在这里使用spread 运算符来传递多个值。第一个参数——我们称之为字符串——是一个包含所有普通字符串的数组(任何插值表达式之间的东西)。

然后,我们使用 ... gather/rest operator 将所有后续参数收集到一个名为 values 的数组中,尽管您当然可以像我们在上面 (value1, value2 etc) 上所做的那样将它们作为单独的命名参数留在字符串参数之后。

function myTaggedLiteral(strings,...values) {
  console.log(strings);
  console.log(values);
}

let someText = 'Neat';
myTaggedLiteral`test ${someText} ${2 + 3}`;
// ["test ", " ", ""]
// ["Neat", 5]

收集到我们的 values 数组中的 argument(s) 是在字符串文字中找到的已经评估的插值表达式的结果。 tagged string literal 就像在计算插值之后但在编译最终字符串值之前的处理步骤,允许您更好地控制从文字生成字符串。我们来看一个创建re-usable templates的例子。

const Actor = {
  name: "RajiniKanth",
  store: "Landmark"
}

const ActorTemplate = templater`<article>
  <h3>${'name'} is a Actor</h3>
  <p>You can find his movies at ${'store'}.</p>

</article>`;

function templater(strings, ...keys) {
  return function(data) {
  let temp = strings.slice();
  keys.forEach((key, i) => {
  temp[i] = temp[i] + data[key];
  });
  return temp.join('');
  }
};

const myTemplate = ActorTemplate(Actor);
console.log(myTemplate);

原始字符串

我们的标签函数接收我们称为strings 的第一个参数,即array。但是还包括一些额外的数据:所有字符串的原始未处理版本。您可以使用.raw 属性访问这些原始字符串值,如下所示:

function showraw(strings, ...values) {
  console.log( strings );
  console.log( strings.raw );
}
showraw`Hello\nWorld`;

如您所见,raw 版本的字符串保留了转义的 \n 序列,而处理后的字符串版本将其视为未转义的真正换行符。 ES6 自带一个内置函数,可以用作字符串文字标签:String.raw(..)。它只是通过strings 的原始版本:

console.log( `Hello\nWorld` );
/* "Hello
World" */

console.log( String.raw`Hello\nWorld` );
// "Hello\nWorld"

【讨论】:

    【解决方案3】:

    如果您仅在问题示例中使用带有占位符的模板文字(例如`Hello ${person.name}`),则结果与仅连接字符串相同。主观上它看起来更好,更容易阅读,特别是对于多行字符串或同时包含 '" 的字符串,因为您不必再​​转义这些字符。

    可读性是一个很棒的功能,但模板最有趣的地方是Tagged template literals

    let person = {name: 'John Smith'}; 
    let tag = (strArr, name) => strArr[0] + name.toUpperCase() + strArr[1];  
    tag `My name is ${person.name}!` // Output: My name is JOHN SMITH!
    

    在此示例的第三行中,调用了一个名为 tag 的函数。模板字符串的内容被拆分为多个变量,您可以在 tag 函数的参数中访问这些变量:文字部分(在此示例中,strArr[0] 的值是 My name is strArr[1] 的值是!) 和替换 (John Smith)。模板文字将被评估为 tag 函数返回的任何内容。

    ECMAScript wiki 列出了一些可能的用例,例如自动转义或编码输入,或本地化。您可以创建一个名为 msg 的标记函数,它会查找像 My name is 这样的文字部分,并将它们替换为当前语言环境的语言的翻译,例如德语:

    console.log(msg`My name is ${person.name}.`) // Output: Mein Name ist John Smith.
    

    标签函数返回的值甚至不必是字符串。您可以创建一个名为 $ 的标记函数,它评估字符串并将其用作查询选择器以返回 DOM 节点的集合,例如 example

    $`a.${className}[href=~'//${domain}/']`
    

    【讨论】:

    • 不错!如果你有另一个模板文字,比如 ${person.message},它会被一起翻译吗?
    • @Rigotti 这取决于msg 函数的实现。您当然也可以翻译替换值。
    • @Beat 整个 ecmascript.org 网站似乎已经关闭。我认为他们无论如何都计划放弃他们的 wiki,所以我用存档版本更新了链接。
    • 我尝试在 chrome 控制台中运行 $a.${className}[href=~'//${domain}/'](并设置在 className=''domain='' 之前,但我没有得到 DOM 节点,而是字符串数组:/(在另一个 hadn ,在 jsfiddle 中,我们在控制台中收到错误:jsfiddle.net/d1fkta76"Uncaught ReferenceError: $ is not defined" - 为什么?
    • @AniketSuryavanshi 下面是模板字符串与连接性能的比较:stackoverflow.com/a/29083467/897024 几年前模板字符串速度较慢,但​​现在看起来它们比连接要快一些。
    【解决方案4】:

    它更简洁,正如 cmets 中所述,它是其他语言的共同特征。 另一件我觉得不错的是换行符,在编写字符串时非常有用。

    let person = {name: 'John Smith', age: 24, greeting: 'Cool!' };
    
    let usualHtmlStr = "<p>My name is " + person.name + ",</p>\n" +
                       "<p>I am " + person.age + " old</p>\n" +
                       "<strong>\"" + person.greeting +"\" is what I usually say</strong>";
    
    
    let newHtmlStr = 
     `<p>My name is ${person.name},</p>
      <p>I am ${person.age} old</p>
      <p>"${person.greeting}" is what I usually say</strong>`;
    
    
    console.log(usualHtmlStr);
    console.log(newHtmlStr);
    

    【讨论】:

    • 我不明白字符串和文字中的换行符是否有重大区别。检查这个es6fiddle.net/i3vj1ldl。文字只放置一个空格而不是换行符
    • 哇,我没有说这是一个主要的区别。文字换行符只是语法糖。这只是为了便于阅读。
    • 但您仍然指出了一个很好的区别。但在接受你的答案之前,我会再等一段时间,以获得更好的答案,这会显示出任何很大的不同(如果有的话!):)
    • @NaeemShaikh 非常抱歉,但文字换行符确实有效。刚刚注意到 ES6Fiddle 只是一种糟糕的测试方式。我将编辑我的答案。
    猜你喜欢
    • 2016-09-19
    • 2016-05-30
    • 2017-10-03
    • 1970-01-01
    • 2021-04-01
    • 1970-01-01
    • 2017-01-15
    相关资源
    最近更新 更多