ECMAScript 6

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接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)

  1. 是为各种数据结构,提供一个统一的、简便的访问接口;

  2. 是使得数据结构的成员能够按某种次序排列;

  3. 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;

其他特性

  1. Symbol数据类型

  2. Set和Map数据结构

  3. 异步操作和Async函数

  4. Proxy和Reflect

  5. 二进制数组

  6. Promise对象

标准库扩展

  1. 字符串的扩展

  2. 函数的扩展

  3. 数值的扩展

  4. 数组的扩展

  5. 对象的扩展

字符串扩展-模板字符串

$('#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

  1. 语言层级支持,无需引入第三方库,不再引用seajs和requirejs就能使用模块

  2. 统一的API,使用保留关键字 import 和 export ,从而在浏览器实现后能最大程度的向下兼容

  3. 精简语法、唯一导出出口(single exports)和循环依赖(cyclic dependencies)的特点

  4. 支持结构的静态分析(静态检查,优化等等)

// 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

vuejs

总结

  • Let和Const更加准确

  • 解构和箭头函数更加精简

  • 标准库扩展

  • 借鉴其他语言实现生成器、遍历器等

  • Promise将最佳实践标准化

  • Module / Class 等让 JS 更适合大型编程

参考

ECMAScript 6入门

[译]JavaScript ES6箭头函数指南

ECMAScript 6新特性印象之二:面对对象和模块化

es6-js-future

es6features

learn-es2015

es6-equivalents-in-es5

web64