谈谈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 相关文章推荐
类似GMAIL的Ajax信息反馈显示
Feb 16 Javascript
百度地图api应用标注地理位置信息(js版)
Feb 01 Javascript
javascript如何创建表格(javascript绘制表格的二种方法)
Dec 10 Javascript
js设置cookie过期当前时间减去一秒相当于立即过期
Sep 04 Javascript
jQuery中大家不太了解的几个方法
Mar 04 Javascript
js实现卡片式项目管理界面UI设计效果
Dec 08 Javascript
node.js 中间件express-session使用详解
May 20 Javascript
js图片轮播插件的封装
Jul 21 Javascript
基于vue2.0实现简单轮播图
Nov 27 Javascript
微信小程序的部署方法步骤
Sep 04 Javascript
详解Vue中组件的缓存
Apr 20 Javascript
前端天气插件tpwidget使用方法详解
Jun 24 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
全局记录程序片段的运行时间 正确找到程序逻辑耗时多的断点
2011/01/06 PHP
php实现字符串首字母转换成大写的方法
2015/03/17 PHP
Yii框架组件的事件机制原理与用法分析
2020/04/07 PHP
IE中jquery.form中ajax提交没反应解决方法分享
2012/09/11 Javascript
jQuery Pagination Ajax分页插件(分页切换时无刷新与延迟)中文翻译版
2013/01/11 Javascript
ie与ff下的event事件使用介绍
2013/11/25 Javascript
js实现全屏漂浮广告移入光标停止移动
2013/12/02 Javascript
JavaScript实现对下拉列表值进行排序的方法
2015/07/15 Javascript
jQuery实现验证年龄简单思路
2016/02/24 Javascript
深入理解Node.js的HTTP模块
2016/10/12 Javascript
AngularJS过滤器filter用法总结
2016/12/13 Javascript
bootstrap table实例详解
2017/01/06 Javascript
详解Node.js中的Async和Await函数
2018/02/22 Javascript
Vue中this.$router.push参数获取方法
2018/02/27 Javascript
vue实现键盘输入支付密码功能
2018/08/18 Javascript
vue2中使用sass并配置全局的sass样式变量的方法
2018/09/04 Javascript
微信小程序自定义导航栏
2018/12/31 Javascript
小程序中this.setData的使用和注意事项
2019/08/28 Javascript
JavaScript Event Loop相关原理解析
2020/06/10 Javascript
[05:08]2014DOTA2国际邀请赛 Hao专访复仇的胜利很爽
2014/07/15 DOTA
[02:53]DOTA2亚洲邀请赛 NewBee战队巡礼
2015/02/03 DOTA
详解Python中的四种队列
2018/05/21 Python
matplotlib给子图添加图例的方法
2018/08/03 Python
Python使用Selenium爬取淘宝异步加载的数据方法
2018/12/17 Python
Python当中的array数组对象实例详解
2019/06/12 Python
Python openpyxl读取单元格字体颜色过程解析
2019/09/03 Python
python如何输出反斜杠
2020/06/18 Python
Python延迟绑定问题原理及解决方案
2020/08/04 Python
CSS3 :not()选择器实现最后一行li去除某种css样式
2016/10/19 HTML / CSS
健康监测猫砂:Pretty Litter
2017/05/25 全球购物
受希腊女神灵感的晚礼服、鸡尾酒礼服和婚纱:THEIA
2018/04/15 全球购物
医学生求职自荐书
2014/06/12 职场文书
大学生创业事迹材料
2014/12/30 职场文书
2015年高三教学工作总结
2015/07/21 职场文书
《半截蜡烛》教学反思
2016/02/19 职场文书
Python内置类型集合set和frozenset的使用详解
2022/04/26 Python