[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>
)
}
}
```