super() 和 super(props) 有什么区别?

一、是什么

super()super(props) 都是 ES6 类继承中调用父类构造函数的语法,在 React 的类组件中有特定的含义。虽然这个话题在函数组件主导的今天看似过时,但它涉及 JavaScript 的核心继承机制,也是理解 React 类组件工作原理的基础知识。

要理解这个问题,需要先回顾 ES6 的 classextends 机制。

二、ES6 类继承基础

super 的本质

在 ES6 中,super 关键字用于调用父类的构造函数。如果一个类使用 extends 继承了另一个类,那么在子类的 constructor必须先调用 super(),然后才能使用 this

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} 发出声音`);
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    // 必须先调用 super,否则报错
    super(name);
    this.breed = breed;
  }

  bark() {
    console.log(`${this.name} 在叫`);
  }
}

const dog = new Dog('旺财', '柴犬');
dog.speak(); // "旺财 发出声音"
dog.bark();  // "旺财 在叫"

不调用 super 会怎样

如果子类有 constructor 但没有调用 super(),JavaScript 会抛出 ReferenceError

class Cat extends Animal {
  constructor(name) {
    // ❌ ReferenceError: Must call super constructor in derived class
    // before accessing 'this' or returning from derived constructor
    this.name = name;
  }
}

这是因为在 ES6 的继承模型中,子类的 this 对象是由父类的构造函数创建的(通过 super() 调用),子类只是在这个基础上进行修改。这与 ES5 基于原型链的继承方式有本质区别。

省略 constructor

如果子类不需要额外的初始化逻辑,可以完全省略 constructor。此时 JavaScript 引擎会自动生成一个默认的构造函数:

class Cat extends Animal {
  // 等价于:
  // constructor(...args) {
  //   super(...args);
  // }
}

三、React 类组件中的 super

React.Component 的构造函数

React 的 Component 基类在构造函数中接收 props 并将其存储:

// React 源码简化版
class Component {
  constructor(props) {
    this.props = props;
    this.state = {};
    // ...
  }
}

当我们创建一个类组件时:

class MyComponent extends React.Component {
  constructor(props) {
    super(props); // 将 props 传递给 Component 的构造函数
    this.state = { count: 0 };
  }
}

super(props) vs super()

两者的区别在于:super(props) 会将 props 传递给父类 React.Component 的构造函数,使得 this.props 在构造函数中可用。而 super() 不传参数,this.props 在构造函数中会是 undefined

class WithProps extends React.Component<{ name: string }> {
  constructor(props: { name: string }) {
    super(props);
    console.log(this.props); // ✅ { name: '张三' }
  }

  render() {
    return <div>{this.props.name}</div>;
  }
}

class WithoutProps extends React.Component<{ name: string }> {
  constructor(props: { name: string }) {
    super();
    console.log(this.props); // ❌ undefined
    console.log(props);      // ✅ { name: '张三' }(参数仍然可用)
  }

  render() {
    // ✅ render 中 this.props 正常,因为 React 在构造后会重新赋值
    return <div>{this.props.name}</div>;
  }
}

为什么 render 中 this.props 始终正常

即使在构造函数中使用 super() 而不是 super(props)this.propsrender 和其他生命周期方法中仍然可以正常访问。这是因为 React 在调用构造函数之后,会立即对实例的 props 进行赋值:

// React 内部逻辑简化
const instance = new MyComponent(props);
instance.props = props; // React 在此处重新赋值

所以 super(props) 只影响构造函数内部能否通过 this.props 访问 props。

四、实际场景演示

构造函数中需要 this.props 的情况

class UserGreeting extends React.Component<{ name: string; isVip: boolean }> {
  greeting: string;

  constructor(props: { name: string; isVip: boolean }) {
    super(props); // 必须传 props

    // 在构造函数中需要使用 this.props
    this.greeting = this.props.isVip
      ? `尊贵的 VIP 用户 ${this.props.name},欢迎回来!`
      : `${this.props.name},你好!`;

    this.state = {
      lastLogin: new Date(),
    };
  }

  render() {
    return <h1>{this.greeting}</h1>;
  }
}

不需要构造函数中访问 this.props

class Counter extends React.Component<{}, { count: number }> {
  constructor(props: {}) {
    super(); // 这里不传 props 也没问题
    this.state = { count: 0 }; // state 初始化不依赖 props
  }

  render() {
    return <div>{this.state.count}</div>;
  }
}

使用类属性语法——根本不需要 constructor

现代 TypeScript/Babel 支持类属性语法,可以完全避开构造函数:

class ModernCounter extends React.Component<
  { initialCount: number },
  { count: number }
> {
  // 类属性语法初始化 state
  state = {
    count: this.props.initialCount,
  };

  // 箭头函数属性自动绑定 this
  handleClick = () => {
    this.setState(prev => ({ count: prev.count + 1 }));
  };

  render() {
    return (
      <div>
        <p>{this.state.count}</p>
        <button onClick={this.handleClick}>+1</button>
      </div>
    );
  }
}

类属性语法在编译时实际上会被转换到构造函数内,但编译器会自动处理 super(props) 的调用,开发者无需操心。

五、最佳实践

类组件中始终使用 super(props)

为了避免在构造函数中因遗忘传递 props 而产生潜在的 bug,推荐统一使用 super(props)

class MyComponent extends React.Component<MyProps, MyState> {
  constructor(props: MyProps) {
    super(props); // ✅ 始终传递 props
    this.state = { /* ... */ };
  }
}

更好的做法——避免使用构造函数

// ✅ 使用类属性语法,不需要构造函数
class MyComponent extends React.Component<MyProps, MyState> {
  state: MyState = {
    count: 0,
    data: null,
  };

  handleClick = () => {
    // ...
  };

  render() {
    return <div>...</div>;
  }
}

最佳做法——使用函数组件

// ✅✅ 函数组件完全不存在 super 的问题
function MyComponent({ initialCount }: { initialCount: number }) {
  const [count, setCount] = useState(initialCount);

  return (
    <div>
      <p>{count}</p>
      <button onClick={() => setCount(c => c + 1)}>+1</button>
    </div>
  );
}

六、常见面试追问

Q:如果不写 constructor,React 是怎么处理的?

A:如果类组件没有定义 constructor,JavaScript 引擎会使用默认构造函数 constructor(...args) { super(...args); },这等效于 super(props),所以一切正常。

Q:super() 不传 props 会导致 bug 吗?

A:在绝大多数情况下不会。this.props 在构造函数以外的地方始终可以正确访问,因为 React 在构造后会重新赋值。只有在构造函数中同时需要 this.props 且使用了 super() 时,才会遇到 this.propsundefined 的问题。

Q:函数组件为什么不需要 super?

A:函数组件就是普通函数,不涉及类继承。props 作为函数参数直接传入,没有 this,也就不存在 super 的问题。这也是函数组件心智模型更简单的原因之一。

七、知识脉络

ES6 class 继承
  └── super() 调用父类构造函数
       ├── super()     → 不传参数,this.props 在 constructor 中为 undefined
       └── super(props) → 传递 props,this.props 在 constructor 中可用

            ├── React 内部会在构造后重新赋值 instance.props = props
            │   └── 所以 render/生命周期中 this.props 始终正常

            ├── 类属性语法可以避开手写 constructor

            └── 函数组件完全不存在这个问题
                └── 这也是 React 推荐函数组件的原因之一

八、总结

写法constructor 中 this.props其他方法中 this.props推荐程度
super(props)✅ 可用✅ 可用类组件中推荐
super()❌ undefined✅ 可用不推荐
不写 constructor✅ 可用更推荐
函数组件不适用不适用最推荐

super()super(props) 的区别本质上是 ES6 继承机制的问题,而不是 React 特有的。在现代 React 开发中,函数组件 + Hooks 已经完全取代了类组件的使用,这个问题更多是作为理解 JavaScript 继承机制和 React 历史的知识点。面试中如果被问到,除了回答技术细节,还应该补充说明函数组件已经消除了这类问题。