题目取得有些长,今天晓明同学遇到了一个问题,说自己定义的state出现了诡异现象,我一看,应该是state定义的位置放错了,果然,没有放在constructor里面,为什么呢,一时语塞,知道跟继承相关,但是要说清楚也说不出来,遂来研究一下。

我们先来认识一下构造函数,在es5中,我们是这样定义构造函数的

  1. // 先定义一个函数,强行叫它构造函数,大写的P也不是必须的,只是约定俗成
  2. function Point(x, y) {
  3. this.x = x; // 构造函数的属性都定义在函数内部
  4. this.y = y; // this指向实例对象
  5. }
  6. // 构造函数的方法都定义在构造函数的原型上
  7. Point.prototype.toString = function () {
  8. return '(' + this.x + ', ' + this.y + ')';
  9. };
  10. // new 一个对象,就OK了
  11. var p = new Point(1, 2); // 实现子类继承父类
  1. <br />看到这里,让我想起了掌众面试官给我出的题,js中new一个对象发生了什么,让我感到尴尬,他是想考察什么,现在就从上面的列子来讲讲,**——ES5中new到底做了些啥?**<br />**当一个构造函数前加上new的时候,背地里来做了四件事:<br />1.生成一个空的对象并将其作为 this;<br />2.将空对象的 __proto__ 指向构造函数的 prototype;<br />3.运行该构造函数;<br />4.如果构造函数没有 return 或者 return 一个返回 this 值是基本类型,则返回this;如果 return 一个引用类型,则返回这个引用类型。

简单解释,就是在ES5的继承中,先创建子类的实例对象this,然后再将父类的方法添加到this上( Parent.apply(this) )。而ES6采用的是先创建父类的实例this(故要先调用 super( )方法),完后再用子类的构造函数修改this

复杂的来讲,我们重点介绍一下第二点和第四点;
proto为原型链,我们可以理解为纽带,没错,妈妈肚子里的脐带,你和你妈是有血缘关系的,你继承了母体了一些基因
prototype为原型,我们可以简单的理解为母体,对,可以叫妈咪,你长得像你妈,都跟这个有关系

第四点好像没有什么说的,过。下面这幅图很清晰的表达new到底干了些什么
image.png

第一行,我们创建了一个空对象obj
第二行,我们将这个空对象的proto成员指向了Base函数对象prototype成员对象
第三行,我们将Base函数对象的this指针替换成obj,然后再调用Base函数,于是我们就给obj对象赋值了一个id成员变量,这个成员变量的值是”base”,关于call函数的用法。

在es6中构造函数的写法(class)

由于要兼容过去的构造函数写法,所以ES6的类其实就是语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用ES6的class改写为

  1. //定义类
  2. class Point {
  3. constructor(x, y) { // 定义构造方法
  4. this.x = x; // this指向实例对象
  5. this.y = y;
  6. }
  7. toString() { // 定义一个方法,注意这里没有function关键字
  8. return '(' + this.x + ', ' + this.y + ')'; // this指向实例对象
  9. }
  10. }

class就是function的另一种写法,本质还是function

  1. class Point {
  2. }
  3. typeof Point // "function" 类的数据类型就是函数
  4. Point === Point.prototype.constructor // true 类本身就指向构造函数

react中class的写法与初始化state 的写法
**
以上呢,我们是讲了es5和es6构造函数实现方面的差异,对于实现的差异其实还没能讲的足够细致和深入,我后期会进行补充,现在让我们回到 react 这个点上来,我们都知道,三大框架的诞生裨益于es6的诞生于发展,熟悉react 开发的同学都应该见过下面这段代码:

  1. // 下面这段代码是antd pro 模板弄下来的代码,我们注意这里state定义的方式
  2. // 直接定义静态属性,这是es7的实现,更为简单,实用
  3. class AdvancedProfile extends Component {
  4. state = { // 定义实例属性,初始化state,需要babel转换实现
  5. operationkey: 'tab1',
  6. stepDirection: 'horizontal',
  7. };
  8. componentDidMount() {
  9. const { dispatch } = this.props;
  10. dispatch({
  11. type: 'profile/fetchAdvanced',
  12. });
  13. this.setStepDirection();
  14. window.addEventListener('resize', this.setStepDirection, { passive: true });
  15. }

刚好最近在使用 京东的 taro ,我们再来看一下这段代码

  1. // 这个是我们熟悉的es6的写法
  2. // 构造函数内定义,这是es6的实现
  3. // react巧妙地运用了这一特性。因为它有一个非常棒的API,那就是extends(继承),react组件就是这么来实现的
  4. class Welcome extends React.Component {
  5. constructor(props) {
  6. super(props); // 调用父类的构造函数,改变this指向
  7. this.state = {name: 'aotu,taro!'};
  8. }
  9. render() {
  10. return <h1>Hello, {this.state.name}</h1>;
  11. }
  12. }

es6规定是只有静态方法,没有静态属性的。但是es7可以定义实例属性,可通过babel转换实现。
要使用es7也很简单,通常安装和配置babel-preset-stage-0即可使用所有的es6/7 新特性
基于上述描述,我们可以参考一下这篇文章:https://blog.csdn.net/u010377383/article/details/77944151
(这篇文章讲述了react随es5,es6,es7语言版本迭代的写法的演进)

说完class构造函数,我们再来讲讲es6中class 的继承,还是看下面这段代码

  1. // 在这里我们用关键字 extend 实现了继承
  2. class Welcome extends React.Component {
  3. constructor(props) {
  4. super(props); // 调用父类的构造函数,改变this指向
  5. this.state = {name: 'aotu,taro!'};
  6. }
  7. render() {
  8. return <h1>Hello, {this.state.name}</h1>;
  9. }
  10. }

妈呀,太晚了,后面再来写了,洗澡睡觉,
本文将着重根据这篇博文来https://www.jianshu.com/p/2255d41d8e8b来展开讲解class的静态方法与静态属性,从而延展到原型对象,原型链,构造函数,把这一连串的东西都整明白,按道理来说,其实这篇文章应该放在js基础上来写,我们把串联的这部分东西放在class与原型上着重详细的梳理一下。