本文转载 : http://www.css88.com/archives/9922
注:本文为 《 JavaScript 完全手册(2018版) 》第2节,你可以查看该手册的完整目录。
ECMAScript 2015,也称为 ES6 或者 ES2015,是 ECMAScript 标准的基础版本。
在标准版本 ECMAScript 5.1 发布4年后发布,还有一个重要的变化是 ECMAScript 标准版本的命名从版本号命名改为了根据发布年号命名。
所以它不应该被命名为 ES6(尽管每个人都这样称呼它),更加准确的命名应该是 ES2015 。
从1999年到2009年,ES5 整整制定了 10 年,因此它也是该语言的基础和非常重要的修订版。 但现在已经过了很多时间,因此讨论 ES5 之前代码的工作原理并不值得。
由于 ES5.1 和 ES6 之间已经过了很长时间,因此 ES6 带来了很多重大的变化,一些重要的新功能以及在开发 JavaScript 程序时建议的最佳实践。 要了解 ES2015 的基本功能,请记住,在此版本中,规范文档从 250 页到 600 页,很长很长。
ES2015 最重要的变化包括:
箭头函数改变了大多数 JavaScript 代码的外观(和工作方式)。 在视觉上,我们来看一下这种简单而受欢迎的变化:
const foo = function foo() {
//...
}
变化为:
const foo = () => {
//...
}
如果函数体内只有一行代码,只需:
const foo = () => doSomething()
此外,如果您只有一个参数,您可以写:
const foo = param => doSomething(param)
这不是一个破坏性的改变,因为常规函数可以继续像以前一样正常工作。
箭头函数的 this 作用域继承自上下文。
对于常规函数,这总是指最近的函数,而使用箭头函数时,这个问题就不存在了,你不需要再次写 var that = this
。
如果你还没很多好的理解箭头函数中的 this 作用域,可以查看: ES2015 中的箭头函数和词法 this以了解更多
Promise 允许我们消除 “回调地狱” ,虽然它们引入了更多的复杂性( ES2017 已经带来了 async
,用更高级别的概念解决了复杂性问题)。
在 ES2015 之前,JavaScript 开发人员已经使用了 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)
使用 promises 重写为:
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'))
更多关于 Promise 的信息,请查看 ES6 Promise 指南 。
Generator(生成器)是一种特殊的函数,它能够暂停自身,并在稍后恢复,允许其他代码同时运行。
代码决定它必须等待,因此它允许其他代码“在队列中”运行,并保持 “当它正在等待的东西” 完成时恢复其操作的权利。
所有这些都是通过一个简单的关键字完成的:yield
。 当生成器包含该关键字时,将暂停执行。
生成器可以包含许多 yield
关键字,因此会多次自行停止,并且由 *function
关键字标识,不要与低级编程语言(如C,C ++或Go)中使用的指针解引用运算符混淆。
生成器在JavaScript中开启了全新的编程范例,允许:
这是一个生成器的例子,它解释了生成器是如何工作的。
function *calculator(input) {
var doubleThat = 2 * (yield (input / 2))
var another = yield (doubleThat)
return (input * doubleThat * another)
}
我们用下面的代码初始化这个 Generator(生成器)函数
const calc = calculator(10)
然后我们在我们的生成器上启动迭代器:
calc.next()
第一次迭代启动迭代器。代码返回 this
对象:
{
done: false
value: 5
}
会发生什么呢?代码运行函数,input = 10
传递给生成器构造函数。 它一直运行直到达到 yield
,并返回 yield
的内容:input / 2 = 5
。所以我们得到了一个值 5,并指示迭代没有完成(函数只是暂停)。
在第二次迭代中,我们传递值 7
:
calc.next(7)
我们得到的结果是:
{
done: false
value: 14
}
7
被赋值给 doubleThat
。
重要提示:您可能认为
input / 2
是参数,但这只是第一次迭代的返回值。我们现在跳过它,并使用新的输入值7
,并将其乘以2
。
然后我们达到第二个 yield
,然后返回 doubleThat
,因此返回值为 14
。
在下一个和最后一个迭代中,我们传入 100
calc.next(100)
我们得到的结果是:
{
done: true
value: 14000
}
当迭代完成时(没有找到更多的 yield
关键字),我们只返回(input * doubleThat * another)
,其数量为 10 * 14 * 100
。
如果你还是没理解,可以看看 用最简单的方式理解 JavaScript 中的 Symbols,Iterators(迭代器),Generators(生成器),Async/Await(异步/等待) 和 Async Iterators(异步迭代器) 这篇文章了解更多详情
var
是传统上的函数作用域。
let
是一个新的变量声明方式,它是块作用域。
在 for
循环中、 if
中或普通块中声明 let
变量不会让该变量 “逃出” 该块,而 var
变量的作用域是函数定义中。
const
就像 let
,但是他是一个固定不变的值。
在 JavaScript 向前发展中,你将很少,甚至不会看到 var 声明,只需要 let
和 const
。
特别是 const
,也许令人惊讶的是,它现在被广泛使用,其中不可变性非常受欢迎。
更多信息请查看:ES6 中的块级作用域及变量声明 let
传统上,JavaScript 是唯一一个基于原型继承的主流语言。 从基于类的语言切换到 JavaScript 的程序员,发现 JavaScript 很令人费解,但是 ES2015 引入了 Classes(类),这些类只是 JavaScript 内部工作的语法糖,但改变了我们构建 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.”
Classes(类) 没有显式的类变量声明,但必须在 构造函数(constructor) 中初始化所有变量。
Classes(类) 有一个叫做 constructor
的特殊方法,当通过 new
初始化一个 class(类) 时会调用它。
可以使用 super()
引用父类。
可以将属性的 getter 声明为
class Person {
get fullName() {
return `${this.firstName} ${this.lastName}`
}
}
setter 以相同的方式编写:
class Person {
set age(years) {
this.theAge = years
}
}
关于 ES6 Classes(类) 的更多信息请查看 面向对象的 JavaScript – 深入了解 ES6 类
在 ES2015 之前,至少有三个主要的模块标准竞争,这些标准使社区分散:
ES2015 将这些标准化为通用格式。
导入模块是通过 import ... from ...
结构完成的:
import * from 'mymodule'
import React from 'react'
import { React, Component } from 'react'
import React as MyLibrary from 'react'
您可以编写模块,并使用 export
关键字将任何内容导出给其他模块使用:
export var foo = 2
export function bar() { /* ... */ }
你可以查看 JavaScript 模块简史 和 ECMAScript 6 Modules(模块)系统及语法详解 来完整了解 JavaScript 模块化进程和 ES6 Modules(模块)的更多信息。
Template Literals(模板字面量)是创建字符串的新语法:
const aString = `A string`
它提供了一种将表达式嵌入到字符串中的方法,通过使用 ${a_variable}
语法有效地进行插值:
const var = 'test'
const string = `something ${var}` //something test
您还可以执行更复杂的表达式:
const string = `something ${1 + 2 + 3}`
const string2 = `something ${foo() ? 'x' : 'y' }`
并且字符串可以跨越多行:
const string3 = `Hey
this
string
is awesome!`
比较一下,我们在 ES2015 之前 实现多行字符串的方式:
var str = 'One\n' +
'Two\n' +
'Three'
函数现在支持默认参数:
const foo = function(index = 0, testing = true) { /* ... */ }
foo()
了解更多关于默认参数的信息,请查看 JavaScript 函数中默认参数
您可以使用展开操作符 ...
展开数组,对象或字符串。
让我们从一个数组示例开始。查看以下内容:
const a = [1, 2, 3]
您可以使用创建一个新数组
const b = [...a, 4, 5, 6]
您还可以使用展开操作符创建一个数组的副本
const c = [...a]
spread operator(展开操作符)也适用于对象。用以下方法克隆对象:
const newObj = { ...oldObj }
使用字符串,spread operator(展开操作符)创建一个数组,其中包含字符串中的每个字符:
const hey = 'hey'
const arrayized = [...hey] // ['h', 'e', 'y']
这个运算符有一些非常有用的应用。最重要的是能够以非常简单的方式使用数组作为函数参数:
const f = (foo, bar) => {}
const a = [1, 2]
f(...a)
在过去,你可以使用 f.apply(null, a)
来做到这一点,但可读性不是很好。
spread operator(展开操作符)对于函数参数的应用还有另一种叫法:Rest Parameters(剩余形参),你可以查看 JavaScript 函数中的参数:Parameters(形参) 和 Arguments(实参) 了解更多的说明。
给定一个对象,您可以只提取一些值并将它们放入命名变量中:
const person = {
firstName: 'Tom',
lastName: 'Cruise',
actor: true,
age: 54, //made up
}
const {firstName: name, age} = person
console.log(name) // "Tom"
console.log(age) // 54
name
和 age
包含了所需的值。
语法也适用于数组:
const a = [1,2,3,4,5]
let [first, second, , , fifth] = a;
console.log(first) // 1
console.log(second) // 2
console.log(fifth) // 5
在 ES2015 中,对象字面量获得了增强。
你不再需要这么做:
const something = 'y'
const x = {
something: something
}
你只需:
const something = 'y'
const x = {
something
}
可以使用指定原型
const anObject = { y: 'y' }
const x = {
__proto__: anObject
}
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
ES5 早在 2009 年就推出了 forEach()
循环。虽然很好,但它们没有办法中断循环(比如使用 break
语句或使用 return
语句),就像 for
循环一样。
ES2015 引入了 for..of
循环,它结合了 forEach
的简洁性和中断循环的能力:
//iterate over the value
for (const v of ['a', 'b', 'c']) {
console.log(v);
}
//get the index as well, using `entries()`
for (const [i, v] of ['a', 'b', 'c'].entries()) {
console.log(i, v);
}
了解 JavaScript 中循环方法的比较,请查看 JavaScript里的循环方法:forEach,for…in,for…of。关于 for..of 循环的更多解释和应用,请查看 理解 JavaScript 中的 for…of 循环
Map 和 Set(以及它们各自的垃圾收集 WeakMap 和 WeakSet )是两个非常流行的数据结构的官方实现(在本手册后续的章节中会着重介绍)。
总之,ES6 一个重大的 ECMAScript 版本,增加了很多新特性,本手册基本涵盖了我们常用的特性,但是还不够深入,查看以下文章,更加深入的了解 ES6 的新特性。部分链接已经在上面的内容中推荐,你可以选择性的查看