ECMAScript是JavaScript基于的标准,通常缩写为ES。了解关于ECMAScript的一切以及ES6(又称ES2015)中添加的功能。
- 箭头函数
- 新的
this
作用域 - Promises
- 生成器
let
和const
- 类
- 模块
- 模板字符串
- 默认参数
- 展开运算符
- 解构赋值
- 增强的对象字面量
- for-of循环
- Map和Set
- 新的字符串方法
- 新的对象方法
ECMAScript 2015,也称为ES6,是ECMAScript标准的基本版本。
与最新的标准修订相隔4年发布,ECMAScript 5.1,它也标志着从版本号转换为年份的变化。
因此不应该称为ES6(尽管每个人都这样称呼),而应该称为ES2015。
ES5的制定历经了10年,从1999年到2009年,因此它也是一次基本而非常重要的语言修订,但现在已经过去了很长时间,没有必要讨论ES5之前的代码是如何工作的了。
由于ES5.1和ES6之间过去了很长时间,因此发布版本中充满了重要的新功能和对JavaScript程序开发中推荐最佳实践的重大变化。要了解ES2015有多么基础,请记住,随着这个版本,规范文档从250页增加到了~600页。
本文描述了最重要的变化。
箭头函数
自引入以来,箭头函数改变了大多数JavaScript代码的外观(和工作原理)。
从视觉上看,这是一个简单而受欢迎的变化,从:
const something = function something() {
//...
}
到
const something = () => {
//...
}
如果函数体只有一行代码,只需:
const something = () => doSomething()
此外,如果只有一个参数,可以写成:
const something = param => doSomething(param)
这并不是一个破坏性的变化,常规的function
仍然像以前一样工作。
新的this
作用域
箭头函数的this
作用域是从上下文继承的。
对于常规的function
,this
始终引用最近的函数,而对于箭头函数,解决了这个问题,您将不再需要编写var that = this
。
Promises
Promises(查看完整的Promises指南)使我们能够消除著名的“回调地狱”,尽管它们引入了更多的复杂性(这在ES2017中使用async
解决了,这是一个更高级的构造函数)。
很多JavaScript开发人员在ES2015之前就已经使用Promise,并且有许多不同的库实现(例如jQuery、q、deferred.js、vow等),标准将其统一起来。
通过使用Promises,您可以将此代码重写为
setTimeout(function() {
console.log('I promised to run after 1s')
setTimeout(function() {
console.log('I promised to run after 2s')
}, 1000)
}, 1000)
如
const wait = () => new Promise((resolve, reject) => {
setTimeout(resolve, 1000)
})
wait().then(() => {
console.log('I promised to run after 1s')
return wait()
})
.then(() => console.log('I promised to run after 2s'))
生成器
生成器是一种特殊类型的函数,它具有暂停和恢复的能力,允许在此期间运行其他代码。
有关详细解释,请参阅完整的JavaScript生成器指南。
let
和const
var
传统上是函数作用域的。
let
是一个新的变量声明,它是块级作用域的。
这意味着在for
循环中,if
内部或纯块内部声明的let
变量不会“逃出”该块,而var
将被提升到函数定义的顶部。
const
与let
非常相似,但是不可变。
在JavaScript中,你会看到很少或几乎没有var
声明了,只有let
和const
。
特别是const
非常常用,因为不变性非常受欢迎。
类
传统上,JavaScript是唯一一种原型继承的主流语言。从基于类的语言切换到JS的程序员发现它很费解,但是ES2015引入了类,它只是语法糖,但是改变了我们构建JavaScript程序的方式。
现在继承变得非常简单,并且与其他面向对象编程语言类似:
class Person {
constructor(name) {
this.name = name
}
hello() {
return 'Hello, I am ' + this.name + '.'
}
}
class Actor extends Person {
hello() {
return super.hello() + ' I am an actor.'
}
}
var tomCruise = new Actor('Tom Cruise')
tomCruise.hello()
(以上程序打印“Hello, I am Tom Cruise. I am an actor.“)
类没有显式的类变量声明,但必须在构造函数中初始化任何变量。
构造函数
类有一个特殊的方法,称为constructor
,在通过new
初始化类时调用该方法。
Super
可以使用super()
引用父类。
取值器和存值器
可以声明属性的取值器:
class Person {
get fullName() {
return `${this.firstName} ${this.lastName}`
}
}
存值器的编写方式相同:
class Person {
set age(years) {
this.theAge = years
}
}
模块
在ES2015之前,至少有3种竞争的模块标准,这导致社区的分裂:
- AMD
- RequireJS
- CommonJS
ES2015将其标准化为一种通用格式。
导入模块
通过import ... from ...
结构导入:
import \* from 'mymodule'
import React from 'react'
import { React, Component } from 'react'
import React as MyLibrary from 'react'
导出模块
您可以编写模块并使用export
关键字将任何内容导出到其他模块:
export var number = 2
export function bar() { /\* ... \*/ }
模板字符串
模板字符串是一种创建字符串的新语法:
const aString = `A string`
它们提供了一种将表达式嵌入字符串的方式,通过${a_variable}
语法有效地插入值:
const joe = 'test'
const string = `something ${joe}` //something test
您还可以执行更复杂的表达式:
const string = `something ${1 + 2 + 3}`
const string2 = `something ${doSomething() ? 'x' : 'y' }`
并且字符串可以跨越多行:
const string3 = `Hey
this
string
is awesome!`
将其与ES2015之前如何处理多行字符串进行比较:
var str = 'One\n' +
'Two\n' +
'Three'
默认参数
函数现在支持默认参数:
const someFunction = function(index = 0, testing = true) { /\* ... \*/ }
someFunction()
展开运算符
您可以使用展开运算符...
展开数组、对象或字符串。
让我们从一个数组示例开始。给定
const a = [1, 2, 3]
您可以使用以下代码创建一个新数组:
const b = [...a, 4, 5, 6]
您还可以使用以下代码创建数组的副本:
const c = [...a]
这对对象也适用。使用以下代码克隆对象:
const newObj = { ...oldObj }
对于字符串,展开运算符将创建一个包含字符串中每个字符的数组:
const hey = 'hey'
const arrayized = [...hey] // ['h', 'e', 'y']
展开运算符有一些非常有用的应用。最重要的应用之一是以非常简单的方式使用数组作为函数参数:
const f = (arg1, arg2) => {}
const a = [1, 2]
f(...a)
(过去,可以使用f.apply(null, a)
来完成,但这并不那么好看和可读)
解构赋值
给定一个对象,您可以提取出其中的一些值,并将它们放入命名变量中:
const person = {
firstName: 'Tom',
lastName: 'Cruise',
actor: true,
age: 54, //虚构的
}
const {firstName: name, age} = person
name
和age
包含所需的值。
该语法也适用于数组:
const a = [1,2,3,4,5]
const [first, second] = a
此语句通过从数组a
中获取索引为0、1、4的项来创建3个新变量:
const [first, second, , , fifth] = a
增强的对象字面量
在ES2015中,对象字面量获得了超级能力。
更简洁的语法来包含变量
不再需要编写
const something = 'y'
const x = {
something: something
}
可以使用以下代码:
const something = 'y'
const x = {
something
}
原型
可以使用以下代码指定原型:
const anObject = { y: 'y' }
const x = {
\_\_proto\_\_: anObject
}
super()
const anObject = { y: 'y', test: () => 'zoo' }
const x = {
\_\_proto\_\_: anObject,
test() {
return super.test() + 'x'
}
}
x.test() //zoox
动态属性
const x = {
['a' + '\_' + 'b']: 'z'
}
x.a\_b //z
for-of循环
ES5在2009年引入了forEach()
循环。虽然好用,但它们没有提供中断的方法,就像for
循环一样。
ES2015引入了for-of循环,它将forEach
的简洁性与中断的能力结合起来:
//遍历值
for (const v of ['a', 'b', 'c']) {
console.log(v);
}
//使用`entries()`以及获取索引
for (const [i, v] of ['a', 'b', 'c'].entries()) {
console.log(i, v);
}
Map和Set
Map和Set(及其各自的垃圾回收WeakMap和WeakSet)是两种非常流行的数据结构的官方实现。
新的字符串方法
任何字符串值都有一些新的实例方法:
repeat()
重复字符串指定的次数:'Ho'.repeat(3) //HoHoHo
codePointAt()
处理无法由单个16位UTF-16单元表示的字符的Unicode代码
新的对象方法
ES6在对象命名空间下引入了几种静态方法:
Object.is()
用于判断两个值是否相同的值Object.assign()
用于浅复制对象Object.setPrototypeOf
设置对象原型