题目取得有些长,今天晓明同学遇到了一个问题,说自己定义的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与原型上着重详细的梳理一下。