对象字面量的简写属性和计算的属性名不可同时使用,原因是简写属性是一种在编译阶段的就会生效的语法糖,而计算的属性名则在运行时才生效;
1、箭头函数本身已经很简洁,但是还可以进一步简写;
2、解构也许确实可以理解为变量声明的一种语法糖,当涉及到多层解构时,其使用非常灵活;
3、学会了模板字符串的高级用法--标记模板字符串;
4、let,const声明的变量同样存在变量提升,理解了TDZ机制
ES6为一些已有的功能提供了非破坏性更新,这类更新中的大部分我们可以理解为语法糖,称之为语法糖,意味着,这类新语法能做的事情其实用ES5也可以做,只是会稍微复杂一些。本章我们将着重讨论这些语法糖,看完之后,可能你会对一些你很熟悉的ES6新语法有不一样的理解。
对象字面量
对象字面量是指以{}形式直接表示的对象,比如下面这样:
var book = {
title: 'Modular ES6',
author: 'Nicolas',
publisher: 'O´Reilly'
}
ES6 为对象字面量的语法带来了一些改进:包括属性/方法的简洁表示,可计算的属性名等等,我们逐一来看:
属性的简洁表示法
你有没有遇到过这种场景,一个我们声明的对象中包含若干属性,其属性值由变量表示,且变量名和属性名一样的。比如下面这样,我们想把一个名为 listeners 的数组赋值给events对象中的listeners属性,用ES5我们会这样做:
var listeners = []
function listen() {}
var events = {
listeners: listeners,
listen: listen
}
ES6则允许我们简写成下面这种形式:
var listeners = []
function listen() {}
var events = { listeners, listen }
怎么样,是不是感觉简洁了许多,使用对象字面量的简洁写法让我们在不影响语义的情况下减少了重复代码。
这是ES6带来的好处之一,它提供了众多更简洁,语义更清晰的语法,让我们的代码的可读性,可维护性大大提升。
可计算的属性名
对象字面量的另一个重要更新是允许你使用可计算的属性名,在ES5中我们也可以给对象添加属性名为变量的属性,一般说来,我们要按下面方法这样做,首先声明一个名为expertise的变量,然后通过person[expertise]这种形式把变量添加为对象person的属性:
var expertise = 'journalism'
var person = {
name: 'Sharon',
age: 27
}
person[expertise] = {
years: 5,
interests: ['international', 'politics', 'internet']
}
ES6 中,对象字面量可以使用计算属性名了,把任何表达式放在中括号中,表达式的运算结果将会是对应的属性名,上面的代码,用ES6可以这样写:
var expertise = 'journalism'
var person = {
name: 'Sharon',
age: 27,
[expertise]: {
years: 5,
interests: ['international', 'politics', 'internet']
}
}
不过需要注意的是,简写属性和计算的属性名不可同时使用。这是因为,简写属性是一种在编译阶段的就会生效的语法糖,而计算的属性名则在运行时才生效。如果你把二者混用,代码会报错。而且二者混用往往还会降低代码的可读性,所以JavaScript在语言层面上限制二者不能混用也是个好事。
var expertise = 'journalism'
var journalism = {
years: 5,
interests: ['international', 'politics', 'internet']
}
var person = {
name: 'Sharon',
age: 27,
[expertise] // 这里会报语法错误
}
遇到以下情景时,可计算的属性名会让我们的代码更简洁:
某个新对象的属性引自另一个对象:
var grocery = {
id: 'bananas',
name: 'Bananas',
units: 6,
price: 10,
currency: 'USD'
}
var groceries = {
[grocery.id]: grocery
}
需构建的对象的属性名来自函数参数。如果使用ES5来处理这种问题,我们需要先声明一个对象字面量,再动态的添加属性,再返回这个对象。下面的例子中,我们创建了一个响应Ajax请求的函数,这个函数的作用在于,请求失败时,返回的对象拥有一个名为error属性及对应的描述,请求成功时,该对象拥有一个名为success属性及对应的描述。
// ES5 写法
function getEnvelope(type, description) {
var envelope = {
data: {}
}
envelope[type] = description
return envelope
}
使用ES6提供的利用计算属性名,更简洁的实现如下:
// ES6 写法
function getEnvelope(type, description) {
return {
data: {},
[type]: description
}
}
对象字面量的属性可以简写,方法其实也是可以的。
方法定义
我们先看看传统上如何定义对象方法,下述代码中,我们构建了一个事件发生器,其中的on方法用以注册事件,emit方法用以执行事件:
var emitter = {
events: {},
on: function (type, fn) {
if (this.events[type] === undefined) {
this.events[type] = []
}
this.events[type].push(fn)
},
emit: function (type, event) {
if (this.events[type] === undefined) {
return
}
this.events[type].forEach(function (fn) {
fn(event)
})
}
}
ES6 的对象字面量方法简写允许我们省略对象方法的function关键字及之后的冒号,改写后的代码如下:
var emitter = {
events: {},
on(type, fn) {
if (this.events[type] === undefined) {
this.events[type] = []
}
this.events[type].push(fn)
},
emit(type, event) {
if (this.events[type] === undefined) {
return
}
this.events[type].forEach(function (fn) {
fn(event)
})
}
}
ES6中的箭头函数可谓大名鼎鼎了,它有一些特别的优点(关于this),可能你和我一样,使用箭头函数很久了,不过有些细节我之前却一直不了解,比如箭头函数的几种简写形式及使用注意事项。
箭头函数
JS中声明的普通函数,一般有函数名,一系列参数和函数体,如下:
function name(parameters) {
// function body
}
普通匿名函数则没有函数名,匿名函数通常会被赋值给一个变量/属性,有时候还会被直接调用:
var example = function (parameters) {
// function body
}
ES6 为我们提供了一种写匿名函数的新方法,即箭头函数。箭头函数不需要使用function关键字,其参数和函数体之间以=>相连接:
var example = (parameters) => {
// function body
}
尽管箭头函数看起来类似于传统的匿名函数,他们却具有根本性的不同:
箭头函数不能被直接命名,不过允许它们赋值给一个变量;
箭头函数不能用做构造函数,你不能对箭头函数使用new关键字;
箭头函数也没有prototype属性;
箭头函数绑定了词法作用域,不会修改this的指向。
最后一点是箭头函数最大的特点,我们来仔细看看。
词法作用域
我们在箭头函数的函数体内使用的this,arguments,super等都指向包含箭头函数的上下文,箭头函数本身不产生新的上下文。下述代码中,我们创建了一个名为timer的对象,它的属性seconds用以计时,方法start用以开始计时,若我们在若干秒后调用start方法,将打印出当前的seconds值。
// ES5
var timer = {
seconds: 0,
start() {
setInterval(function(){
this.seconds++
}, 1000)
}
}
timer.start()
setTimeout(function () {
console.log(timer.seconds)
}, 3500)
0
// ES6
var timer = {
seconds: 0,
start() {
setInterval(() => {
this.seconds++
}, 1000)
}
}
timer.start()
setTimeout(function () {
console.log(timer.seconds)
}, 3500)
// <- 3
第一段代码中start方法使用的是常规的匿名函数定义,在调用时this将指向了window,console出的结果为undefined,想要让代码正常工作,我们需要在start方法开头处插入var self = this,然后替换匿名函数函数体中的this为self,第二段代码中,我们使用了箭头函数,就不会发生这种情况了。
还需要说明的是,箭头函数的作用域也不能通过.call,.apply,.bind等语法来改变,这使得箭头函数的上下文将永久不变。
我们再来看另外一个箭头函数与普通匿名函数的不同之处,你猜猜,下面的代码最终打印出的结果会是什么:
function puzzle() {
return function () {
console.log(arguments)
}
}
puzzle('a', 'b', 'c')(1, 2, 3)
答案是1,2,3,原因是对常规匿名函数而言,arguments指向匿名函数本身。
作为对比,我们看看下面这个例子,再猜猜,打印结果会是什么?
function puzzle() {
return ()=>{
console.log(arguments)
}
}
puzzle('a', 'b', 'c')(1, 2, 3)
答案是a,b,c,箭头函数的特殊性决定其本身没有arguments对象,这里的arguments其实是其父函数puzzle的。
前面我们提到过,箭头函数还可以简写,接下来我们一起看看。
简写的箭头函数
完整的箭头函数是这样的:
var example = (parameters) => {
// function body
}
简写1:
当只有一个参数时,我们可以省略箭头函数参数两侧的括号:
var double = value => {
return value * 2
}
简写2:
对只有单行表达式且,该表达式的值为返回值的箭头函数来说,表征函数体的{},可以省略,return 关键字可以省略,会静默返回该单一表达式的值。
var double = (value) => value * 2
简写3:
上述两种形式可以合并使用,而得到更加简洁的形式
var double = value => value * 2
现在,你肯定学会了箭头函数的基本使用方法,接下来我们再看几个使用示例。
简写箭头函数带来的一些问题
当你的简写箭头函数返回值为一个对象时,你需要用小括号括起你想返回的对象。否则,浏览器会把对象的{}解析为箭头函数函数体的开始和结束标记。
// 正确的使用形式
var objectFactory = () => ({ modular: 'es6' })
下面的代码会报错,箭头函数会把本想返回的对象的花括号解析为函数体,number被解析为label,value解释为没有做任何事情表达式,我们又没有显式使用return,返回值默认是undefined。
[1, 2, 3].map(value => { number: value })
// <- [undefined, undefined, undefined]
当我们返回的对象字面量不止一个属性时,浏览器编译器不能正确解析第二个属性,这时会抛出语法错误。
[1, 2, 3].map(value => { number: value, verified: true })
// <- SyntaxError
解决方案是把返回的对象字面量包裹在小括号中,以助于浏览器正确解析:
[1, 2, 3].map(value => ({ number: value, verified: true }))
/* <- [
{ number: 1, verified: true },
{ number: 2, verified: true },
{ number: 3, verified: true }]
*/
该何时使用箭头函数
其实我们并不应该盲目的在一切地方使用ES6,ES6也不是一定比ES5要好,是否使用主要看其能否改善代码的可读性和可维护性。
箭头函数也并非适用于所有的情况,比如说,对于一个行数很多的复杂函数,使用=>代替function关键字带来的简洁性并不明显。不过不得不说,对于简单函数,箭头函数确实能让我们的代码更简洁。
给函数以合理的命名,有助于增强程序的可读性。箭头函数并不能直接命名,但是却可以通过赋值给变量的形式实现间接命名,如下代码中,我们把箭头函数赋值给变量 throwError,当函数被调用时,会抛出错误,我们可以追溯到是箭头函数throwError报的错。
var throwError = message => {
throw new Error(message)
}
throwError('this is a warning')
<- Uncaught Error: this is a warning
at throwError
如果你想完全控制你的函数中的this,使用箭头函数是简洁高效的,采用函数式编程尤其如此。
[1, 2, 3, 4]
.map(value => value * 2)
.filter(value => value > 2)
.forEach(value => console.log(value))
// <- 4
// <- 6
// <- 8
解构赋值
ES6提供的最灵活和富于表现性的新特性莫过于解构了。一旦你熟悉了,它用起来也很简单,某种程度上解构可以看做是变量赋值的语法糖,可应用于对象,数组甚至函数的参数。
对象解构
为了更好的描述对象解构如何使用,我们先构建下面这样一个对象(漫威迷一定知道这个对象描述的是谁):
// 描述Bruce Wayne的对象
var character = {
name: 'Bruce',
pseudonym: 'Batman',
metadata: {
age: 34,
gender: 'male'
},
batarang: ['gas pellet', 'bat-mobile control', 'bat-cuffs']
}
假如现有有一个名为 pseudonym 的变量,我们想让其变量值指向character.pseudonym,使用ES5,你往往会按下面这样做:
var pseudonym = character.pseudonym
ES6致力于让我们的代码更简洁,通过ES6我们可以用下面的代码实现一样的功能:
var { pseudonym } = character
如同你可以使用var加逗号在一行中同时声明多个变量,解构的花括号内使用逗号可以做一样的事情。
var { pseudonym, name } = character
我们还可以混用解构和常规的自定义变量,这也是解构语法灵活性的表现之一。
var { pseudonym } = character, two = 2
解构还允许我们使用别名,比如我们想把character.pseudonym赋值给变量 alias,可以按下面的语句这样做,只需要在pseudonym后面加上:即可:
var { pseudonym: alias } = character
console.log(alias)
// <- 'Batman'
解构还有另外一个强大的功能,解构值还可以是对象:
var { metadata: { gender } } = character
当然,对于多层解构,我们同样可以赋予别名,这样我们可以通过非常简洁的方法修改子属性的名称:
var { metadata: { gender: characterGender } } = character
在ES5 中,当你调用一个未曾声明的值时,你会得到undefined:
console.log(character.boots)
// <- undefined
console.log(character['boots'])
// <- undefined
使用解构,情况也是类似的,如果你在左边声明了一个右边对象中不存在的属性,你也会得到undefined.
var { boots } = character
console.log(boots)
// <- undefined
对于多层解构,如下述代码中,boots并不存在于character中,这时程序会抛出异常,这就好比你你调用undefined或者null的属性时会出现异常。
var { boots: { size } } = character
// <- Exception
var { missing } = null
// <- Exception
解构其实就是一种语法糖,看以下代码,你肯定就能很快理解为什么会抛出异常了。
var nothing = null
var missing = nothing.missing
// <- Exception
解构也可以添加默认值,如果右侧不存在对应的值,默认值就会生效,添加的默认值可以是数值,字符串,函数,对象,也可以是某一个已经存在的变量:
var { boots = { size: 10 } } = character
console.log(boots)
// <- { size: 10 }
对于多层的解构,同样可以使用默认值
var { metadata: { enemy = 'Satan' } } = character
console.log(enemy)
// <- 'Satan'
默认值和别名也可以一起使用,不过需要注意的是别名要放在前面,默认值添加给别名:
var { boots: footwear = { size: 10 } } = character
对象解构同样支持计算属性名,但是这时候你必须要添加别名,这是因为计算属性名允许任何类似的表达式,不添加别名,浏览器解析时会有问题,使用如下:
var { ['boo' + 'ts']: characterBoots } = character
console.log(characterBoots)
// <- true
还是那句话,我们也不是任何情况下都应该使用解构,语句characterBoots = character[type]看起来比{ [type]: characterBoots } = character语义更清晰,但是当你需要提取对象中的子对象时,解构就很简洁方便了。
我们再看看在数组中该如何使用解构。
数组解构
数组解构的语法和对象解构是类似的。区别在于,数组解构我们使用中括号而非花括号,下面的代码中,通过结构,我们在数组coordinates中提出了变量 x,y 。 你不需要使用x = coordinates[0]这样的语法了,数组解构不使用索引值,但却让你的代码更加清晰。
var coordinates = [12, -7]
var [x, y] = coordinates
console.log(x)
// <- 12
数组解构也允许你跳过你不想用到的值,在对应地方留白即可:
var names = ['James', 'L.', 'Howlett']
var [ firstName, , lastName ] = names
console.log(lastName)
// <- 'Howlett'
和对象解构一样,数组解构也允许你添加默认值:
var names = ['James', 'L.']
var [ firstName = 'John', , lastName = 'Doe' ] = names
console.log(lastName)
// <- 'Doe'
在ES5中,你需要借助第三个变量,才能完成两个变量值的交换,如下:
var left = 5, right = 7;
var aux = left
left = right
right = aux
使用解构,一切就简单多了:
var left = 5, right = 7;
[left, right] = [right, left]
我们再看看函数解构。
函数默认参数
在ES6中,我们可以给函数的参数添加默认值了,下例中我们就给参数 exponent 分配了一个默认值:
function powerOf(base, exponent = 2) {
return Math.pow(base, exponent)
}
箭头函数同样支持使用默认值,需要注意的是,就算只有一个参数,如果要给参数添加默认值,参数部分一定要用小括号括起来。
var double = (input = 0) => input * 2
我们可以给任何位置的任何参数添加默认值。
function sumOf(a = 1, b = 2, c = 3) {
return a + b + c
}
console.log(sumOf(undefined, undefined, 4))
// <- 1 + 2 + 4 = 7
在JS中,给一个函数提供一个包含若干属性的对象字面量做为参数的情况并不常见,不过你依旧可以按下面方法这样做:
var defaultOptions = { brand: 'Volkswagen', make: 1999 }
function carFactory(options = defaultOptions) {
console.log(options.brand)
console.log(options.make)
}
carFactory()
// <- 'Volkswagen'
// <- 1999
不过这样做存在一定的问题,当你调用该函数时,如果传入的参数对象只包含一个属性,另一个属性的默认值会自动失效:
carFactory({ make: 2000 })
// <- undefined
// <- 2000
函数参数解构就可以解决这个问题。
函数参数解构
通过函数参数解构,可以解决上面的问题,这里我们为每一个属性都提供了默认值,单独改变其中一个并不会影响其它的值:
function carFactory({ brand = 'Volkswagen', make = 1999 }) {
console.log(brand)
console.log(make)
}
carFactory({ make: 2000 })
// <- 'Volkswagen'
// <- 2000
不过这种情况下,函数调用时,如果参数为空即carFactory()函数将抛出异常。这种问题可以通过下面的方法来修复,下述代码中我们添加了一个空对象作为options的默认值,这样当函数被调用时,如果参数为空,会自动以{}作为参数。
function carFactory({
brand = 'Volkswagen',
make = 1999
} = {}) {
console.log(brand)
console.log(make)
}
carFactory()
// <- 'Volkswagen'
// <- 1999
除此之外,使用函数参数解构,还可以让你的函数自行匹配对应的参数,看接下来的例子,你就能明白这一点了,我们定义一个名为car的对象,这个对象拥有很多属性:owner,brand,make,model,preferences等等。
var car = {
owner: {
id: 'e2c3503a4181968c',
name: 'Donald Draper'
},
brand: 'Peugeot',
make: 2015,
model: '208',
preferences: {
airbags: true,
airconditioning: false,
color: 'red'
}
}
解构能让我们的函数方便的只使用里面的部分数据,下面代码中的函数getCarProductModel说明了具体该如何使用:
var getCarProductModel = ({ brand, make, model }) => ({
sku: brand + ':' + make + ':' + model,
brand,
make,
model
})
getCarProductModel(car)
解构使用示例
当一个函数的返回值为对象或者数组时,使用解构,我们可以非常简洁的获取返回对象中某个属性的值(返回数组中某一项的值)。比如说,函数getCoordinates()返回了一系列的值,但是我们只想用其中的x,y,我们可以这样写,解构帮助我们避免了很多中间变量的使用,也使得我们代码的可读性更高。
function getCoordinates() {
return { x: 10, y: 22, z: -1, type: '3d' }
}
var { x, y } = getCoordinates()
通过使用默认值,可以减少重复,比如你想写一个random函数,这个函数将返回一个位于min和max之间的值。我们可以分辨设置min默认值为1,max默认值为10,在需要的时候还可以单独改变其中的某一个值:
function random({ min = 1, max = 10 } = {}) {
return Math.floor(Math.random() * (max - min)) + min
}
console.log(random())
// <- 7
console.log(random({ max: 24 }))
// <- 18
解构还可以配合正则表达式使用。看下面这个例子:
function splitDate(date) {
var rdate = /(\d+).(\d+).(\d+)/
return rdate.exec(date)
}
var [ , year, month, day] = splitDate('2015-11-06')
不过当.exec不比配时会返回null,因此我们需要修改上述代码如下:
var matches = splitDate('2015-11-06')
if (matches === null) {
return
}
var [, year, month, day] = matches
下面我们继续来讲讲spread和rest操作符。
剩余参数和拓展符
ES6之前,对于不确定数量参数的函数。你需要使用伪数组arguments,它拥有length属性,却又不具备很多一般数组有的特性。需要通过Array#slice.call转换arguments对象真数组后才能进行下一步的操作:
function join() {
var list = Array.prototype.slice.call(arguments)
return list.join(', ')
}
join('first', 'second', 'third')
// <- 'first, second, third'
对于这种情况,ES6提供了一种更好的解决方案:rest。
剩余参数rest
使用rest, 你只需要在任意JavaScript函数的最后一个参数前添加三个点...即可。当rest参数是函数的唯一参数时,它就代表了传递给这个函数的所有参数。它起到和前面说的.slice一样的作用,把参数转换为了数组,不需要你再对arguments进行额外的转换了。
function join(...list) {
return list.join(', ')
}
join('first', 'second', 'third')
// <- 'first, second, third'
rest参数之前的命名参数不会被包含在rest中,
function join(separator, ...list) {
return list.join(separator)
}
join('; ', 'first', 'second', 'third')
// <- 'first; second; third'
在箭头函数中使用rest参数时,即使只有这一个参数,也需要使用圆括号把它围起来,不然就会报错SyntaxError,使用示例如下:
var sumAll = (...numbers) => numbers.reduce(
(total, next) => total + next
)
console.log(sumAll(1, 2, 5))
// <- 8
上述代码的ES5实现如下:
// ES5的写法
function sumAll() {
var numbers = Array.prototype.slice.call(arguments)
return numbers.reduce(function (total, next) {
return total + next
})
}
console.log(sumAll(1, 2, 5))
// <- 8
拓展运算符
拓展运算符可以把任意可枚举对象转换为数组,使用拓展运算符可以高效处理目标对象,在拓展目前前添加...就可以使用拓展运算符了。下例中...arguments就把函数的参数转换为了数组字面量。
function cast() {
return [...arguments]
}
cast('a', 'b', 'c')
// <- ['a', 'b', 'c']
使用拓展运算符,我们也可以把字符串转换为由每一个字母组成的数组:
[...'show me']
// <- ['s', 'h', 'o', 'w', ' ', 'm', 'e']
使用拓展运算符,还可以拼合数组:
function cast() {
return ['left', ...arguments, 'right']
}
cast('a', 'b', 'c')
// <- ['left', 'a', 'b', 'c', 'right']
var all = [1, ...[2, 3], 4, ...[5], 6, 7]
console.log(all)
// <- [1, 2, 3, 4, 5, 6, 7]
这里我还想再强调一下,拓展运算符不仅仅适用于数组和arguments对象,对任意可迭代的对象都可以使用。迭代也是ES6新提出的一个概念,在 Iteration and Flow Control这一章,我们将详细叙述迭代。
Shifting和Spreading
当你想要抽出一个数组的前一个或者两个元素时,常用的解决方案是使用.shift.尽管是函数式的,下述代码在第一次看到的时候却不好理解,我们使用了两次.slice从list中抽离出两个不同的元素。
var list = ['a', 'b', 'c', 'd', 'e']
var first = list.shift()
var second = list.shift()
console.log(first)
// <- 'a'
在ES6中,结合使用拓展和解构,可以让代码的可读性更好:
var [first, second, ...other] = ['a', 'b', 'c', 'd', 'e']
console.log(other)
// <- ['c', 'd', 'e']
除了对数组进行拓展,你同样可以对函数参数使用拓展,下例展示了如何添加任意数量的参数到multiply函数中。
function multiply(left, right) {
return left * right
}
var result = multiply(...[2, 3])
console.log(result)
// <- 6
向在数组中一样,函数参数中的拓展运算符同样可以结合常规参数一起使用。下例中,print函数结合使用了rest,普通参数,和拓展运算符:
function print(...list) {
console.log(list)
}
print(1, ...[2, 3], 4, ...[5])
// <- [1, 2, 3, 4, 5]
下表总结了,拓展运算符的常见使用方法:
使用示例 ES5 ES6
Concatenation [1, 2].concat(more) [1, 2, ...more]
Push an array onto list list.push.apply(list, items) list.push(...items)
Destructuring a = list[0], other = list.slice(1) [a, ...other] = list
new and apply new (Date.bind.apply(Date, [null,2015,31,8])) new Date(...[2015,31,8])
模板字符串
模板字符串是对常规JavaScript字符串的重大改进,不同于在普通字符串中使用单引号或者双引号,模板字符串的声明需要使用反撇号,如下所示:
var text = This is my first template literal
因为使用的是反撇号,你可以在模板字符串中随意使用单双引号了,使用时不再需要考虑转义,如下:
var text = I'm "amazed" at these opportunities!
模板字符串具有很多强大的功能,可在其中插入JavaScript表达式就是其一。
在字符串中插值
通过模板字符串,你可以在模板中插入任何JavaScript表达式了。当解析到表达式时,表达式会被执行,该处将渲染表达式的值,下例中,我们在字符串中插入了变量name:
var name = 'Shannon'
var text = Hello, ${ name }!
console.log(text)
// <- 'Hello, Shannon!'
模板字符串是支持任何表达式的。使用模板字符串,代码将更容易维护,你无须再手动连接字符串和JavaScript表达式了。
看下面插入日期的例子,是不是又直观又方便:
The time and date is ${ new Date().toLocaleString() }.
// <- 'the time and date is 8/26/2015, 3:15:20 PM'
表达式中还可以包含数学运算符:
The result of 2+3 equals ${ 2 + 3 }
// <- 'The result of 2+3 equals 5'
鉴于模板字符串本身也是JavaScript表达式,我们在模板字符串中还可以嵌套模板字符串;
This template literal ${
is ${ 'nested' }}!
// <- 'This template literal is nested!'
模板字符串的另外一个优点是支持多行字符串;
多行文本模板
在ES6之前,如果你想表现多行字符串,你需要使用转义,数组拼合,甚至使用使用注释符做复杂的hacks.如下所示:
var escaped =
'The first line\n
A second line\n
Then a third line'
var concatenated =
'The first line\n' 'A second line\n'
'Then a third line'
var joined = [
'The first line',
'A second line',
'Then a third line'
].join('\n')
应用ES6,这种处理就简单多了,模板字符串默认支持多行:
var multiline =
The first line A second line Then a third line
当你需要返回的字符串基于html和数据生成,使用模板字符串是很简洁高效的,如下所示:
var book = {
title: 'Modular ES6',
excerpt: 'Here goes some properly sanitized HTML',
tags: ['es6', 'template-literals', 'es6-in-depth']
}
var html = ${ book.title }
) .join('\n ') }