ES6类型扩展-模板字面量

JS的字符串的功能一直以来都特别有限,缺乏很多特性,比如不能直接表示多行字符串、字符串格式化、HTML转义等等。ES6通过模板字面量方式进行了填补,它通过全新的方法解决了这些问题。

基础用法

模板字面量使用反引号``标识。

let s = `hello world`
console.log(s) // 'hello world'

// 如果字符串中想要包含反引号,需要使用反斜杠(\)转义
let s2 = `\`hello world\``
console.log(s2) // '`hello world`'

多行字符串

在ES6之前,要想表示多行字符串,开发者通常需要手动加入\n换行符。
JavaScript有一个语法bug,在换行符后面添加反斜杠,可以承接下一行的代码,虽然也起到了换行的作用,但不建议这么做。

let s = 'hello \n'
+'world'
console.log(s)
// 'hello'
// 'world'


let s2 = 'hello \n\
world'
console.log(s2)
// 'hello'
// 'world'

使用模板字面量可以轻松创建多行字符串,反引号内的空白符会作为字符串的一部分。

let s = `hello
world`

console.log(s)
// 'hello'
// 'world'


let s1 = `hello
    world`
console.log(s1)
// hello
//    world

由于空白符会作为字符串的一部分,所以通过缩进对齐文本时,第一行可以留空,然后缩进后面的内容。

let s = `
<p>
  <span>hello world</span>
</p>`.trim()

console.log(s)
// <p>
//   <span>hello world</span>
// </p>

变量占位符

变量占位符允许将任何有效的JS表达式嵌入到模板字面量中,并将其结果输出为字符串的一部分。变量占位符用${}表示。

let s = 'hello', n = 1.3;
function fn() {
  return 'world'
}

console.log(`${s} ${fn()} ${Math.cell(n)}`)
// 'hello world 2'

模板字面量可以嵌入到另一个模板字面量内部

let s = 'world'
console.log(`hello ${`${s}!`}`)

// 'hello world!'

标签模板

标签模板可以说是模板字面量最强大的地方。标签指的是模板字面量第一个反引号前面标注的字符串,模板就是两个反引号之间的部分。

let s = tag`Hello world`

tag是应用到Hello world模板字面量上的标签

定义标签

标签可以是一个函数:函数的第一个参数是数组,包含了模板字面量中的原始字符串;其他参数是每一个占位符的解释值。

let name = 'Xiaowang', country = 'China'
let s = tag`My name is ${name}, I am from ${country}`

如果有一个tag函数做为模板字面量的标签,那么它会接收3个参数:

  1. 第一个参数是数组,包含了3个原始字符串,即['My name is', ', I am from', '']
  2. 第二个参数是变量name的解释值,即Xiaowang
  3. 第三个参数是变量country的解释值,即China
let name = 'Xiaowang', country = 'China'
let s = tag`My name is ${name}, I am from ${country}`

// 等价于
let s = tag(['My name is', ', I am from', ''], 'Xiaowang', 'China')

标签函数通常使用不定参数特性来定义占位符,从而简化数据处理的过程

function tag(literals, ...substitutions) {
  // 返回一个字符串
}

下面定义一个tag标签函数,模拟模板字面量的默认行为。

function tag(literals, ...substitutions) {
  let ret = ''
  // 使用substitutions的数量进行循环,而不是literals的数量
  for (let i = 0; i < substitutions.length; i++) {
	ret += literals[i]
	ret += substitutions[i]
  }
  // 添加literals的最后一个元素
  ret += literals[literals.length - 1]
  return ret
}

let name = 'Xiaowang', country = 'China'
let s = tag`My name is ${name}, I am from ${country}`
console.log(s) // 'My name is Xiaowang, I am from China'

...可以把不定参数表示成数组。上面的示例通过遍历substitutions数组(substitutions的元素个数始终比literals少1),交替组合literals和substitutions中的元素,最后加上literals的最后一个元素,完成了模拟模板字面量的默认行为。

应用

应用一:过滤HTML字符串,防止用户输入恶意内容

function saferHTML(literals, ...substitutions) {
  let ret = ''
  for (let i = 0; i < substitutions.length; i++) {
    let s = String(substitutions[i])
	ret += literals[i]
	ret += s.replace(/&/g, "&amp;")
            .replace(/</g, "&lt;")
            .replace(/>/g, "&gt;");
  }

  ret += literals[literals.length - 1]
  return ret
}

let content = '<script>alert(1)</script>' // 用户输入
let msg = saferHTML`${content} is safe` // 安全处理
console.log(msg)
// &lt;script&gt;alert(1)&lt;/script&gt; is safe

应用二:多语言转换(国际化处理)

function i18n(literals, ...substitutions) {
  // 数据转换
}

let name = 'Xiaowang', country = 'China'
let ret = i18n`My name is ${name}, I am from ${country}!`
// 我的名字是小王,我来自中国

应用三:模板引擎

模板字符串本身并不能取代模板引擎,它没有条件判断和循环处理这些功能,但可以通过标签函数,自己添加这些功能

function tpl(literals, ...substitutions) {
  // 通过匹配特殊字符处理数据
}

let ret = tpl`
  <ul>
    #for item in ${books}
      <li><i>#{item.title}</i> by #{item.author}</li>
    #end
  </ul>`

raw()

String.raw()方法常作为模板字面量的处理函数,它的第一个参数应该是一个具有raw属性的对象,且raw属性的值应该是一个数组。

String.raw({ raw: 'test' }, 0, 1, 2);// 't0e1s2t'

// 等同于
String.raw({ raw: ['t','e','s','t'] }, 0, 1, 2);
posted @ 2021-09-29 11:34  wmui  阅读(54)  评论(0编辑  收藏  举报