## 第四步: ES6速成教程
最好的學習ES6的方法,是為每一個ES6示例提供一個等價的ES5實現。外面已經有不少介紹ES6的文章,本文將只講其中一些。
### Modules(Import)
~~~
// ES6
import React from 'react';
import {Route, DefaultRoute, NotFoundRoute} from 'react-router';
// ES5
var React = require('react');
var Router = require('react-router');
var Route = Router.Route;
var DefaultRoute = Router.DefaultRoute;
var NotFoundRoute = Router.NotFoundRoute;
~~~
使用ES6中的[解構賦值(destructuring assignment)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment),我們能導入模塊的子集,這對于像*react-router*和*underscore*這樣不止輸出一個函數的模塊尤其有用。
需要注意的是ES6 import的優先級很高,所有的依賴模塊都會在模塊代碼執行之前加載,也就是說,你無法像在CommonJS一樣有條件的加載模塊。之前我嘗試在一個if-else條件里import模塊,結果失敗了。
想了解`import`的更多細節,可訪問它的[MDN頁面](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import)。
### Modules(Export)
~~~
// ES6
function Add(x) {
return x + x;
}
export default Add;
// ES5
function Add(x) {
return x + x;
}
module.exports = Add;
~~~
想學習ES6模塊的更多細節,這里有兩篇文章[ECMAScript 6 modules](http://www.2ality.com/2014/09/es6-modules-final.html)和[Understanding ES6 Modules](http://www.sitepoint.com/understanding-es6-modules/)。
### Classes
ES6 class只不過是現有的基于原型繼承機制的一層語法糖,了解這個事實之后,`class`關鍵字對你來說就不再像一個其它語言的概念了。
~~~
// ES6
class Box {
constructor(length, width) {
this.length = length;
this.width = width;
}
calculateArea() {
return this.length * this.width;
}
}
let box = new Box(2, 2);
box.calculateArea(); // 4
// ES5
function Box(length, width) {
this.length = length;
this.width = width;
}
Box.prototype.calculateArea = function() {
return this.length * this.width;
}
var box = new Box(2, 2);
box.calculateArea(); // 4
~~~
另外,ES6中還可以用`extends`關鍵字來創建子類。
~~~
class MyComponent extends React.Component {
// Now MyComponent class contains all React component methods
// such as componentDidMount(), render() and etc.
}
~~~
了解ES6 class更多信息可查看[Classes in ECMAScript 6](http://www.2ality.com/2015/02/es6-classes-final.html)這篇博文。
### JS?`var`與`let`
這兩個關鍵字唯一的區別是,`var`的作用域在最近的函數塊中,而`let`的作用域在最近的塊語句中——它可以是一個函數、一個for循環,或者一個if語句塊。
這里有個很好的[示例](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let),來自MDN:
~~~
var a = 5;
var b = 10;
if (a === 5) {
let a = 4; // The scope is inside the if-block
var b = 1; // The scope is inside the function
console.log(a); // 4
console.log(b); // 1
}
console.log(a); // 5
console.log(b); // 1
~~~
一般來說,`let`是塊作用域,`var`是函數作用域。
### 箭頭函數(=> fat arrow)
一個箭頭函數表達式與函數表達式相比有更簡短的語法,以及從語法上綁定了`this`值。
~~~
// ES6
[1, 2, 3].map(n => n * 2); // [2, 4, 6]
// ES5
[1, 2, 3].map(function(n) { return n * 2; }); // [2, 4, 6]
~~~
> 注意:如果參數只有一個,圓括號是可選的,到底是否強制使用取決于你,不過有些人認為去掉括號是壞的實踐,有些人則無所謂。
除了更短的語法,箭頭函數還有什么用途呢?
考慮下面這個示例,它來自于我將這個項目轉換為使用ES6之前的代碼:
~~~
$.ajax({ type: 'POST', url: '/api/characters', data: { name: name, gender: gender } })
.done(function(data) {
this.setState({ helpBlock: data.message });
}.bind(this))
.fail(function(jqXhr) {
this.setState({ helpBlock: jqXhr.responseJSON.message });
}.bind(this))
.always(function() {
this.setState({ name: '', gender: '' });
}.bind(this));
~~~
上面的每個函數都創建了自己的`this`作用域,不綁定外層`this`的話我們是無法在示例中調用`this.setState`的,因為函數作用域的`this`一般是*undefined*。
當然,它有繞過的方法,比如將`this`賦值給一個變量,比如`var self = this`,然后在閉包里用`self.setState`代替`this.setState`即可。
而使用等價的ES6代碼的話,我們沒有必要如此麻煩:
~~~
$.ajax({ type: 'POST', url: '/api/characters', data: { name: name, gender: gender } })
.done((data) => {
this.setState({ helpBlock: data.message });
})
.fail((jqXhr) => {
this.setState({ helpBlock: jqXhr.responseJSON.message });
})
.always(() => {
this.setState({ name: '', gender: '' });
});
~~~
ES6的講解就到此為止了,下面讓我們看看React,到底是什么讓它如此特殊。
- 前言
- 概述
- 第一步:新建Express項目
- 第二步:構建系統
- 第三步:項目結構
- 第四步: ES6速成教程
- 第五步: React速成教程
- 第六步:Flux架構速成教程
- 第七步:React路由(客戶端)
- 第八步:React路由(服務端)
- 第九步:Footer和Navbar組件
- 第十步:Socke.IO – 實時用戶數
- 第十一步:添加Character的組件
- 第十二步:數據庫模式
- 第十三步:Express API 路由(1/2)
- 第十五步:Home組件
- 第十四步:Express API 路由(2/2)
- 第十六步:角色(資料)組件
- 第十七步:Top 100 組件
- 第十八步:Stats組件
- 第十九步:部署
- 第二十步: 附加資源
- 總結