[TOC]
# Working with the latest JS features in React
# 在React 里使用最新的JS特性
As I said in the introduction, React is mainly written with modern JavaScript (ES6, ES7, and ES8). If you want to take advantage of React, there are some modern JS features that you should master to get the best results for your React applications. In this first recipe, we are going to cover the essential JS features so you are ready and can start working on your first React application.
在第一個配方中,我們將介紹必要的JS特性,以便您準備好并開始處理您的第一個React應用程序。
## [Spread Operator](https://gist.github.com/sebmarkbage/07bbe37bc42b6d4aef81)
Spread Operator 即 3 個點 `...`,有幾種不同的使用方法。
可用于組裝數組。
```
const todos = ['Learn dva'];
[...todos, 'Learn antd']; // ['Learn dva', 'Learn antd']
```
也可用于獲取數組的部分項。
```
const arr = ['a', 'b', 'c'];
const [first, ...rest] = arr;
rest; // ['b', 'c']
// With ignore
const [first, , ...rest] = arr;
rest; // ['c']
```
還可收集函數參數為數組。
```
function directions(first, ...rest) {
console.log(rest);
}
directions('a', 'b', 'c'); // ['b', 'c'];
```
代替 apply
```
function foo(x, y, z) {}
const args = [1,2,3];
// 下面兩句效果相同
foo.apply(null, args);
foo(...args);
```
對于 Object 而言,用于組合成新的 Object 。(ES2017 stage-2 proposal)
```
const foo = {
a: 1,
b: 2,
};
const bar = {
b: 3,
c: 2,
};
const d = 4;
const ret = { ...foo, ...bar, d }; // { a:1, b:3, c:2, d:4 }
```
此外,在 JSX 中 Spread Operator 還可用于擴展 props,詳見 [Spread Attributes](https://github.com/dvajs/dva-knowledgemap#spread-attributes)。
[spread-operator](https://github.com/dvajs/dva-knowledgemap#spread-operator)
## class示例
`class`定義了一個“類”,可以看到里面有一個 `constructor` 方法,這就是構造方法,而this關鍵字則代表實例對象。簡單地說,constructor內定義的方法和屬性是實例對象自己的,而constructor外定義的方法和屬性則是所有實例對象可以共享的。
class之間可以通過 `extends`關鍵字實現繼承,這比ES5的通過修改原型鏈實現繼承,要清晰和方便很多。上面定義了一個Cat類,該類通過extends關鍵字,繼承了Animal類的所有屬性和方法。
`super`關鍵字,它指代父類的實例(即父類的this對象)。子類必須在constructor方法中調用super方法,否則新建實例時會報錯。這是因為子類沒有自己的this對象,而是繼承父類的this對象,然后對其進行加工。如果不調用super方法,子類就得不到this對象。
```
import React from 'react';
export class Master extends React.Component<any,any> {
// 沒有寫上constructor,默認會生成一個空的構造函數
constructor(props) {
super(props);
this.state = {
loading:false
};
}
// 只是作為演示,函數前面添加一個static關鍵字,表明這是一個靜態方法,不會被實例繼承,只能通過類來調用
static foo(){
console.log(100)
}
componentDidMount(){
//...
}
render(){
return (
<div>Master</div>
)
}
}
```
現在能理解了:
定義一個繼承 `React.Component` 的 `Master` 的類,構造方法 `constructor()` 里定義 `this.state` 私有屬性,而 `constructor` 外的 `componentDidMou
nt` 和 `render` 方法是繼承到的 `React.Component` 的方法。
## 為什么子類要 `super(props)`
假設在 **es5 要實現繼承**,首先定義一個父類:
```
//父類
function sup(name) {
this.name = name
}
//定義父類原型上的方法
sup.prototype.printName = function (){
console.log(this.name)
}
//現在再定義他 sup 的子類,繼承sup的屬性和方法:
function sub(name,age){
sup.call(this,name) //調用call方法,繼承sup超類屬性
this.age = age
}
sub.prototype = new sup() //把子類sub的原型對象指向父類的實例化對象,這樣即可以繼承父類sup原型對象上的屬性和方法
sub.prototype.constructor = sub //這時會有個問題子類的constructor屬性會指向sup,手動把constructor屬性指向子類sub
//這時就可以在父類的基礎上添加屬性和方法了
sub.prototype.printAge = function (){
console.log(this.age)
}
```
這時調用父類生成一個實例化對象:
```
let jack = new sub('jack',20)
jack.printName() //輸出 : jack
jack.printAge() //輸出 : 20
```
這就是es5中實現繼承的方法。
**而在es6中實現繼承**:
```
class Sup {
constructor(name) {
this.name = name
}
printName() {
console.log(this.name)
}
}
class Sub extends Sup{
constructor(name,age) {
super(name) // super代表父類的構造函數
this.age = age
}
printAge() {
console.log(this.age)
}
}
let jack = new Sub('jack',20)
jack.printName() //輸出 : jack
jack.printAge() //輸出 : 20
```
> 子類構造函數中必須要先調用 `super()` !
> 否則出錯:`Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor`
>
對比 es5 和 es6 可以發現在 es5 實現繼承,在 es5 中實現繼承:
* 首先得先調用函數的 `call` 方法把父類的屬性給繼承過來
* 通過 `new` 關鍵字繼承父類原型的對象上的方法和屬性
* 最后再通過手動指定 `constructor` 屬性指向子類對象
而在 es6 中實現繼承,直接調用 `super(name)`,`super` 是代替的是[父類的構造函數](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#Constructor),`super(name)`相當于`Sup.prototype.constructor.call(this, name)`。
### React 中的 super(props)
在ES6中,在子類的 `constructor` 中必須先調用 `super` 才能引用`this
`
最后,可以看下 React 文檔,里面有一段
> Class components should always call the base constructor with props.
```
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
```
* 如果你用到了`constructor`就必須寫 `super()`,是用來初始化 `this` 的,可以綁定事件到`this`上;
* 如果你在`constructor`中要使用 `this.props` ,就必須給 `super` 加參數:`super(props)`;
* 無論有沒有 `constructor`,在render中this.props都是可以使用的,這是React自動附帶的;
```
class HelloMessage extends React.Component{
render (){
return (
<div>nice to meet you! {this.props.name}</div>
);
}
}
// 不過這種只是用 render 的情況,使用一般的 ES6 函數寫會更簡便:
const HelloMessage = (props)=>(
<div>nice to meet you! {this.props.name}</div>
)
```
# React 綁定 this
> https://zhuanlan.zhihu.com/p/29266705
比較好的兩種方法:
1. 可以使用箭頭函數
```
class Foo extends React.Component {
handleClick = ()=>{
this.setState({ xxx:aaa })
}
render () {
return (
// 不要像這樣: <button onClick={(e)=>this.handleClick(e)}>
<button onClick={this.handleClick}>
Click me
</button>
)
}
}
```
2. constructor 中手動 bind
```
class Foo extends React.Component {
constructor(props){
super(props);
// 只執行一次,只會 bind 一次
// this.handleClick = this.handleClick.bind(this);
this.handleClick = () => {
this.setState({ xxx:aaa })
}
}
// handleClick() {
// this.setState({ xxx:aaa })
// }
render () {
return (
<button onClick={this.handleClick}>
Click me
</button>
)
}
}
```