ES6简介
ECMAScript 6(以下简称ES6)是JavaScript语言的下一代标准,于2015年6月批准通过。ECMAScript6的目标,是使得JavaScript语言可以用来编写复杂的大型应用程序,成为企业级开发语言。让代码更加准确,更易于阅读。
ECMAScript是JavaScript语言的国际标准,JavaScript是ECMAScript的实现(ES是规范,JS是实现)。在日常场合,这两个词是可以互换的。
环境支持
目前还没有任何一款浏览器支持ES6特性
Firefox 49支持93%;Chrome 52支持98%
Node.js对ES6的支持度比浏览器更高
ES6 to ES5转换器
Traceur转码器
Traceur允许将ES6代码直接插入网页
(1)在网页头部加载Traceur库文件
(2)script标签的type属性的值是module,而不是text/javascript,编译器会自动将所有type=module的代码编译为ES5,然后再交给浏览器执行
<!DOCTYPE html>
<html>
<head>
<title>Hello, World!</title>
<script src="traceur.js"></script>
<script src="BrowserSystem.js"></script>
<script src="bootstrap.js"></script>
</head>
<body>
<script type="module">
class Greeter {
constructor(message) {
this.message = message;
}
greet() {
let element = document.querySelector('#message');
element.textContent = this.message;
}
}
let greeter = new Greeter('Hello World!');
greeter.greet();
</script>
<h1 id="message"></h1>
</body>
</html>
Babel转码器
Babel可以将ES6代码转为ES5代码;得益于强大的Babel的存在,使我们可以方便的使用ES6的语法,而不必考虑浏览器支持的问题。
// 转码前
input.map(item => item + 1);
// 转码后
input.map(function (item) {
return item + 1;
});
ES6特性
新语法
let和const
let声明变量,只在let命令所在的代码块内有效
为什么需要块级作用域?
1、内层变量可能会覆盖外层变量
var tmp = new Date();
function f() {
console.log(tmp);
if (false) {
var tmp = "hello world";
}
}
f(); // undefined
2、用来计数的循环变量泄露为全局变量
var s = 'hello';
for (var i = 0; i < s.length; i++) {
console.log(s[i]);
}
console.log(i); // 5
const声明常量,只在声明所在的块级作用域内有效
const PI = 3.1415;
PI // 3.1415
PI = 3;
// TypeError: Assignment to constant variable.
if (true) {
const MAX = 5;
}
MAX // Uncaught ReferenceError: MAX is not defined
尝试去改变用 const 声明的常量时,就会报错
引用第三方库的时声明的变量,用 const 来声明可以避免未来不小心重命名而导致出现bug
const monent = require('moment')
解构赋值(Destructuring)
从数组和对象中提取值,对变量进行赋值。本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。
var a = 1;
var b = 2;
var c = 3;
写成
var [a, b, c] = [1, 2, 3];
//提取值
var jsonData = {
id: 42,
status: "OK",
data: [867, 5309]
};
let { id, status, data: number } = jsonData;
console.log(id, status, number);
// 42, "OK", [867, 5309]
//遍历Map结构
var map = new Map();
map.set('first', 'hello');
map.set('second', 'world');
for (let [key, value] of map) {
console.log(key + " is " + value);
}
// first is hello
// second is world
箭头函数(Fat arrow functions)
允许使用 =>
定义函数,箭头函数自动绑定当前上下文 this,更简洁的语法和与父作用域共享关键字this
var f = v => v;
//等同于
var f = function(v) {
return v;
};
function getVerifiedToken(selector) {
return getUsers(selector)
.then(function (users) { return users[0]; })
.then(verifyUser)
.then(function (user, verifiedToken) { return verifiedToken; })
.catch(function (err) { log(err.stack); });
}
使用箭头函数
function getVerifiedToken(selector) {
return getUsers(selector)
.then(users => users[0])
.then(verifyUser)
.then((user, verifiedToken) => verifiedToken)
.catch(err => log(err.stack));
}
function
和{}
都消失了,所有的回调函数都只出现在了一行里。当只有一个参数时,
()
也消失了(rest参数是一个例外,如(...args) => ...)。当
{}
消失后,return
关键字也跟着消失了。单行的箭头函数会提供一个隐式的return。
胖箭头函数不产生属于它自己上下文的this
$('.current-time').each(function () {
var self = this;
setInterval(function () {
$(self).text(Date.now());
}, 1000);
});
$('.current-time').each(function () {
setInterval(() => $(this).text(Date.now()), 1000);
});
生成器(Generator)
Generator 是一种可以停止并在之后重新进入的函数。生成器的环境(绑定的变量)会在每次执行后被保存,下次进入时可继续使用。function关键字后面有个*
号,函数体内可以使用yield
语句进行流程控制。
function* gen() {
yield 'hello';
yield 'world';
return true;
}
var iter = gen();
iter.next()
// { value: 'hello', done: false }
iter.next()
// { value: 'world', done: false }
iter.next()
// { value: undefined, done: true }
使用for...of语句时不需要使用next方法
function* fibonacci() {
let [prev, curr] = [0, 1];
for (;;) {
[prev, curr] = [curr, prev + curr];
yield curr;
}
}
for (let n of fibonacci()) {
if (n > 1000) break;
console.log(n);
}
遍历器(Iterator)
Iterator是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)
是为各种数据结构,提供一个统一的、简便的访问接口;
是使得数据结构的成员能够按某种次序排列;
Iterator接口主要供
for...of
使用
使用TypeScript的写法,遍历器接口(Iterable)、指针对象(Iterator)和next方法返回值的规格可以描述如下
interface Iterable {
[Symbol.iterator]() : Iterator,
}
interface Iterator {
next(value?: any) : IterationResult,
}
interface IterationResult {
value: any,
done: boolean,
}
修饰器(Decorator)
修饰器(Decorator)是一个函数,用来修改类的行为。这是ES7的一个提案,目前Babel转码器已经支持。修饰器对类的行为的改变,是代码编译时发生的,而不是在运行时
@decorator
class A {}
// 等同于
class A {}
A = decorator(A) || A;
其他特性
Symbol数据类型
Set和Map数据结构
异步操作和Async函数
Proxy和Reflect
二进制数组
Promise对象
标准库扩展
字符串的扩展
函数的扩展
数值的扩展
数组的扩展
对象的扩展
字符串扩展-模板字符串
$('#result').append(
'There are <b>' + basket.count + '</b> ' +
'items in your basket, ' +
'<em>' + basket.onSale +
'</em> are on sale!'
);
$('#result').append(`
There are <b>${basket.count}</b> items
in your basket, <em>${basket.onSale}</em>
are on sale!
`);
函数扩展-函数默认值
在ES6之前,不能直接为函数的参数指定默认值,只能采用变通的方法,ES6允许为函数的参数设置默认值,即直接写在参数定义的后面。
function log(x){
x = x || 'hello'
console.log(x)
}
log()
function log(x = 'hello'){
console.log(type)
}
log()
函数扩展-rest参数
ES6引入rest参数(形式为“...变量名”),用于获取函数的多余参数。rest参数之后不能再有其他参数(即只能是最后一个参数)。
function add(...types){
console.log(types)
}
add('a', 'b', 'c') //["a", "b", "c"]
面向对象和模块化
Class
JavaScript语言的传统方法是通过构造函数,定义并生成新对象。写法跟传统的面向对象语言差异很大。
类的继承,Class 之间可以通过extends关键字实现继承,这比ES5的通过修改原型链实现继承,要清晰和方便很多
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function () {
return '(' + this.x + ', ' + this.y + ')';
};
var p = new Point(1, 2);
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); // 调用父类的constructor(x, y)
this.color = color;
}
toString() {
return this.color + ' ' + super.toString(); // 调用父类的toString()
}
}
super关键字
(1)作为函数调用时(即super(...args)),super代表父类的构造函数。
(2)作为对象调用时(即super.prop或super.method()),super代表父类
模块化
import、export
语言层级支持,无需引入第三方库,不再引用seajs和requirejs就能使用模块
统一的API,使用保留关键字 import 和 export ,从而在浏览器实现后能最大程度的向下兼容
精简语法、唯一导出出口(single exports)和循环依赖(cyclic dependencies)的特点
支持结构的静态分析(静态检查,优化等等)
// a.js如下
import {bar} from './b.js';
console.log('a.js');
console.log(bar);
export let foo = 'foo';
// b.js
import {foo} from './a.js';
console.log('b.js');
console.log(foo);
export let bar = 'bar';
babel-node a.js
//结果
b.js
undefined
a.js
bar
由于a.js的第一行是加载b.js,所以先执行的是b.js。而b.js的第一行又是加载a.js,这时由于a.js已经开始执行了,所以不会重复执行,而是继续往下执行b.js,所以第一行输出的是b.js。
接着,b.js要打印变量foo,这时a.js还没执行完,取不到foo的值,导致打印出来是undefined。b.js执行完,开始执行a.js,这时就一切正常了。
ES6应用
组件框架转向 ES6开发
angular2
react
Aurelia
...
总结
Let和Const更加准确
解构和箭头函数更加精简
标准库扩展
借鉴其他语言实现生成器、遍历器等
Promise将最佳实践标准化
Module / Class 等让 JS 更适合大型编程