谈谈JavaScript中super(props)的重要性


Posted in Javascript onFebruary 12, 2019

我听说 Hooks 最近很火。讽刺的是,我想用一些关于 class 组件的有趣故事来开始这篇文章。你觉得如何?

本文中这些坑对于你正常使用 React 并不是很重要。 但是假如你想更深入的了解它的运作方式,就会发现实际上它们很有趣。

开始第一个。

首先在我的职业生涯中写过的super(props) 自己都记不清:

class Checkbox extends React.Component {
 constructor(props) {
  super(props);
  this.state = { isOn: true };
 }
 // ...
}

当然,在类字段提案 (class fields proposal) 中建议让我们跳过这个开头:

class Checkbox extends React.Component {
 state = { isOn: true };
 // ...
}

在2015年 React 0.13 增加对普通类的支持时,曾经打算用这样的语法。定义constructor和调用super(props) 始终是一个临时的解决方案,可能要等到类字段能够提供在工程学上不那么反人类的替代方案。

不过还是让我们回到前面这个例子,这次只用ES2015的特性:

class Checkbox extends React.Component {
 constructor(props) {
  super(props);
  this.state = { isOn: true };
 }
 // ...
}

为什么我们要调用super? 可以调用它吗? 如果必须要调用,不传递prop参数会发生什么? 还有其他参数吗? 接下来我们试一试:

在 JavaScript 中,super 指的是父类的构造函数。(在我们的示例中,它指向React.Component 的实现。)

重要的是,在调用父类构造函数之前,你不能在构造函数中使用this。 JavaScript 是不会让你这样做的:

class Checkbox extends React.Component {
 constructor(props) {
  // 这里还不能用 `this` 
  super(props);
  // 现在可以用了
  this.state = { isOn: true };
 }
 // ...
}

为什么 JavaScript 在使用this之前要先强制执行父构造函数,有一个很好的理由能够解释。 先看下面这个类的结构:

class Person {
 constructor(name) {
  this.name = name;
 }
}
class PolitePerson extends Person {
 constructor(name) {
  this.greetColleagues(); //这行代码是无效的,后面告诉你为什么
  super(name);
 }
 greetColleagues() {
  alert('Good morning folks!');
 }
}

如果允许在调用super之前使用this的话。一段时间后,我们可能会修改greetColleagues,并在提示消息中添加Personname

greetColleagues() {
  alert('Good morning folks!');
  alert('My name is ' + this.name + ', nice to meet you!');
 }

但是我们忘记了super()在设置this.name之前先调用了this.greetColleagues()。 所以此时this.name还没有定义! 如你所见,像这样的代码很难想到问题出在哪里。

为了避免这类陷阱,JavaScript 强制要求:如果想在构造函数中使用this,你必须首先调用super。 先让父类做完自己的事! 这种限制同样也适用于被定义为类的 React 组件:

constructor(props) {
  super(props);
  // 在这里可以用 `this`
  this.state = { isOn: true };
 }

这里又给我们留下了另一个问题:为什么要传props参数?

你可能认为将props传给super是必要的,这可以使React.Component的构造函数可以初始化this.props

// Inside React
class Component {
 constructor(props) {
  this.props = props;
  // ...
 }
}

这与正确答案很接近了 —— 实际上它就是这么做的。

但是不知道为什么,即便是你调用super时没有传递props参数,仍然可以在render和其他方法中访问this.props。 (不信你可以亲自去试试!)

这是究竟是为什么呢? 事实证明,在调用构造函数后,React也会在实例上分配props

// Inside React
 const instance = new YourComponent(props);
 instance.props = props;

因此,即使你忘记将props传给super(),React 仍然会在之后设置它们。 这是有原因的。

当 React 添加对类的支持时,它不仅仅增加了对 ES6 类的支持。它的目标是尽可能广泛的支持类抽象。 目前还不清楚 ClojureScript、CoffeeScript、ES6、Fable、Scala.js、TypeScript或其他解决方案是如何相对成功地定义组件的。 所以 React 故意不关心是否需要调用super()—— 即使是ES6类。

那么这是不是就意味着你可以写super()而不是super(props)呢?

可能不行,因为它仍然是令人困惑的。 当然,React 稍后会在你的构造函数运行后分配this.props, 但是在调用super() 之后和构造函数结束前这段区间内this.props仍然是未定义的:

// Inside React
class Component {
 constructor(props) {
  this.props = props;
  // ...
 }
}
// Inside your code
class Button extends React.Component {
 constructor(props) {
  super(); //我们忘记了传递 props 参数
  console.log(props);   // {}
  console.log(this.props); // undefined 
 }
 // ...
}

如果这种情况发生在从构造函数调用的某个方法中,可能会给调试工作带来很大的麻烦。 这就是为什么我建议总是调用super(props) ,即使在没有必要的情况之下:

class Button extends React.Component {
 constructor(props) {
  super(props); // 传递了 props 参数
  console.log(props);   // {}
  console.log(this.props); // {}
 }
 // ...
}

这样就确保了能够在构造函数退出之前设置好this.props

最后一点是长期以来 React 用户总是感到好奇的。

你可能已经注意到,当你在类中使用Context API时(无论是旧版的contextTypes或在 React 16.6中新添加的 contextType API),context 会作为第二个参数传递给构造函数。

那么为什么我们不写成super(props, context) 呢? 我们可以这样做,但是使用context的频率较低,所以这个坑并没有那么多影响。

根据类字段提案的说明,这些坑大部分都会消失。 如果没有显式构造函数,则会自动传递所有参数。 这允许在像state = {} 这样的表达式中包含对this.propsthis.context的引用(如果有必要的话)。

而有了 Hooks 之后,我们甚至不再有superthis。不过这是另外一个的话题了。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。如果你想了解更多相关内容请查看下面相关链接

Javascript 相关文章推荐
仿jQuery的siblings效果的js代码
Aug 09 Javascript
EasyUI的treegrid组件动态加载数据问题的解决办法
Dec 11 Javascript
上传图片预览JS脚本 Input file图片预览的实现示例
Oct 23 Javascript
EasyUi datagrid 实现表格分页
Feb 10 Javascript
JavaScript中神奇的call()方法
Mar 12 Javascript
jquery+css3实现网页背景花瓣随机飘落特效
Aug 17 Javascript
JS实现简单的右下角弹出提示窗口完整实例
Jun 21 Javascript
微信小程序 基础知识css样式media标签
Feb 15 Javascript
微信小程序商品到详情的实现
Jun 27 Javascript
bootstrap table表格客户端分页实例
Aug 07 Javascript
JavaScript实现微信红包算法及问题解决方法
Apr 26 Javascript
JS使用正则表达式判断输入框失去焦点事件
Oct 16 Javascript
微信小程序实现的自定义分享功能示例
Feb 12 #Javascript
图文讲解用vue-cli脚手架创建vue项目步骤
Feb 12 #Javascript
实例分析编写vue组件方法
Feb 12 #Javascript
详解vue引入子组件方法
Feb 12 #Javascript
vue组件之间数据传递的方法实例分析
Feb 12 #Javascript
vue添加class样式实例讲解
Feb 12 #Javascript
Vue中使用canvas方法总结
Feb 12 #Javascript
You might like
mysql 中InnoDB和MyISAM的区别分析小结
2008/04/15 PHP
php array_walk 对数组中的每个元素应用用户自定义函数详解
2016/11/18 PHP
Json_decode 解析json字符串为NULL的解决方法(必看)
2017/02/17 PHP
JavaScript 无符号右移赋值操作
2009/04/17 Javascript
js操作select控件的几种方法
2010/06/02 Javascript
JS面向对象基础讲解(工厂模式、构造函数模式、原型模式、混合模式、动态原型模式)
2014/08/16 Javascript
Javascript的比较汇总
2016/07/25 Javascript
js当前页面登录注册框,固定div,底层阴影的实例代码
2016/10/04 Javascript
JavaScript控制输入框中只能输入中文、数字和英文的方法【基于正则实现】
2017/03/03 Javascript
Vue.js实现多条件筛选、搜索、排序及分页的表格功能
2020/11/24 Javascript
微信小程序实现缓存根据不同的id来进行设置和读取缓存
2017/06/12 Javascript
ReactJS实现表单的单选多选和反选的示例
2017/10/13 Javascript
js原生实现移动端手指滑动轮播图效果的示例
2018/01/02 Javascript
vue实现密码显示隐藏切换功能
2018/02/23 Javascript
解决layui前端框架 form表单,table表等内置控件不显示的问题
2018/08/19 Javascript
原生JS实现图片懒加载之页面性能优化
2019/04/26 Javascript
一文快速详解前端框架 Vue 最强大的功能
2019/05/21 Javascript
跟老齐学Python之有容乃大的list(1)
2014/09/14 Python
Python解析json之ValueError: Expecting property name enclosed in double quotes: line 1 column 2(char 1)
2017/07/06 Python
想学python 这5本书籍你必看!
2018/12/11 Python
Python 离线工作环境搭建的方法步骤
2019/07/29 Python
Python3 合并二叉树的实现
2019/09/30 Python
Python JSON编解码方式原理详解
2020/01/20 Python
VScode连接远程服务器上的jupyter notebook的实现
2020/04/23 Python
Europcar葡萄牙:葡萄牙汽车和货车租赁
2017/10/13 全球购物
德国机场停车位比较和预订网站:Ich-parke-billiger
2018/01/08 全球购物
急诊科护士自我鉴定
2013/10/14 职场文书
宣传策划类求职信范文
2014/01/31 职场文书
商业房地产广告语
2014/03/13 职场文书
倡议书格式模板
2014/05/13 职场文书
工作证明格式及范本
2014/09/12 职场文书
2014党员民主评议个人思想剖析发言
2014/09/19 职场文书
宝宝满月宴答谢词
2015/09/30 职场文书
Python字符串对齐方法使用(ljust()、rjust()和center())
2021/04/26 Python
Python中使用Opencv开发停车位计数器功能
2022/04/04 Python
Mysql中mvcc各场景理解应用
2022/08/05 MySQL