谈谈JavaScript中super(props)的重要性
作者:疯狂的技术宅 发布时间:2024-04-30 09:56:11
我听说 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
,并在提示消息中添加Person
的name
:
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.props
或this.context
的引用(如果有必要的话)。
而有了 Hooks 之后,我们甚至不再有super
或this
。不过这是另外一个的话题了。
来源:https://segmentfault.com/a/1190000018084870


猜你喜欢
- SQL Server 的扩展存储过程,其实就是一个普通的 Windows DLL,只不过按照某种规则实现了某些函数而已。近日在写一个扩展存储
- Flask-sqlalchemy是关于flask一个针对数据库管理的。文中我们采用一个关于员工显示例子。首先,我们创建SQLALCHEMY对
- 一开始没看懂stddev是什么参数,找了一下,在tensorflow/python/ops里有random_ops,其中是这么写的:def
- 敲了一个错误的mysql命令, 想取消怎么办? 如果用ctrl + c, 就直接退出了。怎么办呢?来看看:mysql> show ta
- 之前我在《各类Excel表格批量合并问题的实现思路与案例》一文中演示了各种常见的表格合并的需求,但VBA复制粘贴的需求却没有演示,今天我演示
- Create trigger tri_wk_CSVHead_History on wk_CSVHead_History --声明一个tri_
- 任务说明:编写一个钱币定位系统,其不仅能够检测出输入图像中各个钱币的边缘,同时,还能给出各个钱币的圆心坐标与半径。效果代码实现Canny边缘
- Python是一种非常实用的高级编程语言,它的易读性和简洁性使其成为初学者的首选语言。然而,Python的功能远不止于此,它的强大库支持使其
- dssaa 问:求一个如奥运倒计时牌那样显示的倒计时程序突然想到这样的一个小程序,不知道能不能做,比如我要在我的主页上挂一个页面,上面只显示
- 本文实例讲述了Python实现计算文件MD5和SHA1的方法。分享给大家供大家参考,具体如下:不多说,直接源码:#file md5impor
- 论坛经常有人会问到用CSS如何美化Select标签,其实但凡你看到很酷的都是用javascript来实现的。昨天试着做了一下,基本实现的初级
- 纪要本文用于记录学习 Python 过程中遇到的一些小问题,如果遇到的是比较大的问题会单独开页面分析学习处处有坑1. 文件读取 open#
- 本文实例讲述了Python深拷贝与浅拷贝用法。分享给大家供大家参考,具体如下:1、对象的赋值对象的赋值实际上是对象之间的引用:当创建一个对象
- 定义列表和其他类型的列表稍有不同,它由两部分组成:名称和定义。DT 指定名称,为内联元素。DD 指定定义,为块级元素。标准属性id, cla
- 配置文件如下,下面对配置文件进行一一解释"""Django settings for film1_manage
- 如果你是一名Web Developer,而且还知道CSS Sprite这个词,请先去搜索一下,也许你正在使用这个技术,但只是不知道它的名字罢
- Access爱好者以会VBa为荣。我觉得这不是好现象。vba只是vb的子集,有着很多限制,比如不支持继承,不支持指针,不支持子界类型等。使用
- 本文实例为大家分享了JavaScript实现简易购物车的具体代码,供大家参考,具体内容如下代码:<!DOCTYPE html>&
- 问题setInterval 是间隔调用,与之类似的还有 setTimeout。这两个 API 通常用来做 ajax 短连接轮询数据。比如有一
- 本文实例讲述了PHP递归调用数组值并用其执行指定函数的方法。分享给大家供大家参考。具体分析如下:以下为wordpress原代码,为了偷懒,简