题目取得有些长,今天晓明同学遇到了一个问题,说自己定义的state出现了诡异现象,我一看,应该是state定义的位置放错了,果然,没有放在constructor里面,为什么呢,一时语塞,知道跟继承相关,但是要说清楚也说不出来,遂来研究一下。
我们先来认识一下构造函数,在es5中,我们是这样定义构造函数的
// 先定义一个函数,强行叫它构造函数,大写的P也不是必须的,只是约定俗成function Point(x, y) {this.x = x; // 构造函数的属性都定义在函数内部this.y = y; // this指向实例对象}// 构造函数的方法都定义在构造函数的原型上Point.prototype.toString = function () {return '(' + this.x + ', ' + this.y + ')';};// new 一个对象,就OK了var p = new Point(1, 2); // 实现子类继承父类
<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到底干了些什么
第一行,我们创建了一个空对象obj
第二行,我们将这个空对象的proto成员指向了Base函数对象prototype成员对象
第三行,我们将Base函数对象的this指针替换成obj,然后再调用Base函数,于是我们就给obj对象赋值了一个id成员变量,这个成员变量的值是”base”,关于call函数的用法。
在es6中构造函数的写法(class)
由于要兼容过去的构造函数写法,所以ES6的类其实就是语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用ES6的class改写为
//定义类class Point {constructor(x, y) { // 定义构造方法this.x = x; // this指向实例对象this.y = y;}toString() { // 定义一个方法,注意这里没有function关键字return '(' + this.x + ', ' + this.y + ')'; // this指向实例对象}}
class就是function的另一种写法,本质还是function
class Point {}typeof Point // "function" 类的数据类型就是函数Point === Point.prototype.constructor // true 类本身就指向构造函数
react中class的写法与初始化state 的写法
**
以上呢,我们是讲了es5和es6构造函数实现方面的差异,对于实现的差异其实还没能讲的足够细致和深入,我后期会进行补充,现在让我们回到 react 这个点上来,我们都知道,三大框架的诞生裨益于es6的诞生于发展,熟悉react 开发的同学都应该见过下面这段代码:
// 下面这段代码是antd pro 模板弄下来的代码,我们注意这里state定义的方式// 直接定义静态属性,这是es7的实现,更为简单,实用class AdvancedProfile extends Component {state = { // 定义实例属性,初始化state,需要babel转换实现operationkey: 'tab1',stepDirection: 'horizontal',};componentDidMount() {const { dispatch } = this.props;dispatch({type: 'profile/fetchAdvanced',});this.setStepDirection();window.addEventListener('resize', this.setStepDirection, { passive: true });}
刚好最近在使用 京东的 taro ,我们再来看一下这段代码
// 这个是我们熟悉的es6的写法// 构造函数内定义,这是es6的实现// react巧妙地运用了这一特性。因为它有一个非常棒的API,那就是extends(继承),react组件就是这么来实现的class Welcome extends React.Component {constructor(props) {super(props); // 调用父类的构造函数,改变this指向this.state = {name: 'aotu,taro!'};}render() {return <h1>Hello, {this.state.name}</h1>;}}
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 的继承,还是看下面这段代码
// 在这里我们用关键字 extend 实现了继承class Welcome extends React.Component {constructor(props) {super(props); // 调用父类的构造函数,改变this指向this.state = {name: 'aotu,taro!'};}render() {return <h1>Hello, {this.state.name}</h1>;}}
妈呀,太晚了,后面再来写了,洗澡睡觉,
本文将着重根据这篇博文来https://www.jianshu.com/p/2255d41d8e8b来展开讲解class的静态方法与静态属性,从而延展到原型对象,原型链,构造函数,把这一连串的东西都整明白,按道理来说,其实这篇文章应该放在js基础上来写,我们把串联的这部分东西放在class与原型上着重详细的梳理一下。
