为什么在super()之前不允许这样做

20

我一直在使用React js进行编程。我读到在ES6类中访问'this'我们需要先调用super(props),我想知道为什么。我找到的答案主要是讲Javascript无法知道除非调用超类,否则'this'是什么。我想知道这是什么意思,因为在构造函数外部,'this'被认可,我们不会每次调用super(props)。

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { /* initial state */ };
  }
}

我认为,在调用super()之前,实例并未被创建,这意味着在那之前this指针不会指向任何东西。如果我错了,请有经验的人纠正我。 - U.P
我看到过代码中在 super({properties}) 之前使用 'this.{property}' 的情况。这是因为在这种情况下,父级的属性被使用了吗? - user4790067
你必须调用super()。 - Руслан
@U.P,你说如果你错了就要纠正你——那么,你错了 :) 当你实例化一个类时,首先发生的是被实例化的类的原型被克隆,这个克隆体成为新的实例。只有在实例已经存在但尚未初始化的状态下,才会按顺序调用原型链中每个构造函数,从Object构造函数向使用new运算符调用的构造函数。 - Davide Cannizzo
@U.P,在子类构造函数中执行其余代码之前需要调用super构造函数,以确保实例完全初始化。 - Davide Cannizzo
3个回答

21

很遗憾,这真的很复杂。


简短地说:在ES6中,子类中在super()调用之前访问this是不允许的,因为this是在基类中创建的,因此需要使用super()来初始化它。

有关更多信息,请参阅15.6.2分配和初始化实例1。作者是为数不多详细解释这一点的人之一。

以下是上述书籍1中的相关示例。

在底层,大致如下。

// Base class: this is where the instance is allocated
function Person(name) {
    // Performed before entering this constructor:
    this = Object.create(new.target.prototype);

    this.name = name;
}
···

function Employee(name, title) {
    // Performed before entering this constructor:
    this = uninitialized;

    this = Reflect.construct(Person, [name], new.target); // (A)
        // super(name);

    this.title = title;
}

8
构造函数默认会返回'this'对象。根据面向对象编程的概念,子类通过super()调用始终从父类继承'this'对象。所以如果我们在子类中尝试使用this但没有使用super调用,则会引发错误。
如果我们从子类中返回除了'this'之外的任何内容,则不需要使用super()调用。
我举了一些简单的例子来解释这个概念。
例子1:
class A {
  constructor() {
    this.a = 0;
  }
}

class B extends A {
    constructor() {
        console.log(this);
    }
}
const instanceA = new A();
console.log(instanceA) // A {a: 0}
const instanceB = new B();
console.log(instanceB) // Error: Must call super constructor in derived class before 
accessing 'this' or returning from derived constructor 

示例2

class A {
  constructor() {
    this.a = 0;
  }
}

class B extends A {
    constructor() {
      return {b: 3}
    }
}
const instanceA = new A();
console.log(instanceA) // A {a: 0}
const instanceB = new B();
console.log(instanceB) // Object {b: 3}

example 3

class A {
  constructor() {
    this.a = 0;
  }
}

class B extends A {
    constructor() {
      super()
    }
}

const instanceB = new B();
console.log(instanceB) // B {a: 0}

7
构造函数是用于创建和初始化使用类创建的对象的特殊方法。在一个类中只能有一个名为“constructor”的特殊方法。如果类包含多个构造函数方法,则会抛出SyntaxError。构造函数可以使用super关键字调用父类的构造函数。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes

这意味着如果你有 class MyComponent extends React.Component,你总是需要调用 super() 来定义它。

如果你没有指定构造函数方法,会使用默认构造函数。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/constructor#Default_constructors

在子类开始配置this之前,应该先调用超类的构造函数来完成this的配置。否则,超类构造函数可能会被子类修改this。超类不应该知道子类的情况。这就是为什么在构造函数中调用super()之前应该先访问this的原因。

30
我觉得这两个引文根本没有回答问题,而你的评论只是重复了事实而没有解释它。 - Evgenia Karunus
2
好的。那我再补充几句。为了在子类开始配置this之前完成this的配置,应该首先调用超类的构造函数。否则,超类构造函数可能会被子类修改this。超类不应该知道子类的任何信息。这就是为什么构造函数中的super()调用应该在访问this之前。 - antono
5
如果你没有编写"super()"方法,为什么不会自动调用它?这背后有一个很好的原因吗? - alex
@Alex,子类的构造函数可能会使用与超类构造函数不同的参数。如果只有构造函数不带参数时才显式调用super()将是不一致的(而且也很难检测 - 例如,如果它使用arguments)。但说实话,我认为ES6正在破坏JavaScript,而在调用super之前不能使用this就是其中许多原因之一。 - B T

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接