super

super 关键字用于访问对象文字或类的 [[Prototype]] 上的属性,或调用超类的构造函数。

¥The super keyword is used to access properties on an object literal or class's [[Prototype]], or invoke a superclass's constructor.

super.propsuper[expr] 表达式在 classes对象字面量 中的任何 方法定义 中都有效。super(...args) 表达式在类构造函数中有效。

¥The super.prop and super[expr] expressions are valid in any method definition in both classes and object literals. The super(...args) expression is valid in class constructors.

Try it

语法

¥Syntax

js
super([arguments]) // calls the parent constructor.
super.propertyOnParent
super[expression]

描述

¥Description

super 关键字有两种使用方式:作为 "函数调用" (super(...args)),或作为 "属性查找"(super.propsuper[expr])。

¥The super keyword can be used in two ways: as a "function call" (super(...args)), or as a "property lookup" (super.prop and super[expr]).

注意:super 是一个关键字,这些是特殊的语法结构。super 不是指向原型对象的变量。尝试读取 super 本身就是 SyntaxError

¥Note: super is a keyword and these are special syntactic constructs. super is not a variable that points to the prototype object. Attempting to read super itself is a SyntaxError.

js
const child = {
  myParent() {
    console.log(super); // SyntaxError: 'super' keyword unexpected here
  },
};

在派生类(带有 extends)的 构造函数 主体中,super 关键字可能会显示为 "函数调用" (super(...args)),必须在使用 this 关键字之前和构造函数返回之前调用它。它调用父类的构造函数并绑定父类的公共字段,之后派生类的构造函数可以进一步访问和修改 this

¥In the constructor body of a derived class (with extends), the super keyword may appear as a "function call" (super(...args)), which must be called before the this keyword is used, and before the constructor returns. It calls the parent class's constructor and binds the parent class's public fields, after which the derived class's constructor can further access and modify this.

"属性查找" 形式可用于访问对象文字或类的 [[Prototype]] 的方法和属性。在类的主体中,super 的引用可以是超类的构造函数本身,也可以是构造函数的 prototype,具体取决于执行上下文是实例创建还是类初始化。有关更多详细信息,请参阅示例部分。

¥The "property lookup" form can be used to access methods and properties of an object literal's or class's [[Prototype]]. Within a class's body, the reference of super can be either the superclass's constructor itself, or the constructor's prototype, depending on whether the execution context is instance creation or class initialization. See the Examples section for more details.

请注意,super 的引用是由声明 super 的类或对象文字决定的,而不是调用该方法的对象。因此,取消绑定或重新绑定方法不会更改其中 super 的引用(尽管它们确实更改了 this 的引用)。你可以将 super 视为类或对象文字范围中的变量,方法在其上创建闭包。(但也要注意,正如上面所解释的,它实际上不是一个变量。)

¥Note that the reference of super is determined by the class or object literal super was declared in, not the object the method is called on. Therefore, unbinding or re-binding a method doesn't change the reference of super in it (although they do change the reference of this). You can see super as a variable in the class or object literal scope, which the methods create a closure over. (But also beware that it's not actually a variable, as explained above.)

当通过 super 设置属性时,该属性将改为在 this 上设置。

¥When setting properties through super, the property is set on this instead.

示例

¥Examples

在课堂上使用 super

¥Using super in classes

此代码片段取自 类样本 (现场演示)。这里调用 super() 是为了避免重复 RectangleSquare 之间共有的构造函数部分。

¥This code snippet is taken from the classes sample (live demo). Here super() is called to avoid duplicating the constructor parts' that are common between Rectangle and Square.

js
class Rectangle {
  constructor(height, width) {
    this.name = "Rectangle";
    this.height = height;
    this.width = width;
  }
  sayName() {
    console.log(`Hi, I am a ${this.name}.`);
  }
  get area() {
    return this.height * this.width;
  }
  set area(value) {
    this._area = value;
  }
}

class Square extends Rectangle {
  constructor(length) {
    // Here, it calls the parent class's constructor with lengths
    // provided for the Rectangle's width and height
    super(length, length);

    // Note: In derived classes, super() must be called before you
    // can use 'this'. Moving this to the top causes a ReferenceError.
    this.name = "Square";
  }
}

超级调用静态方法

¥Super-calling static methods

你还可以在 static 方法上调用 super。

¥You are also able to call super on static methods.

js
class Rectangle {
  static logNbSides() {
    return "I have 4 sides";
  }
}

class Square extends Rectangle {
  static logDescription() {
    return `${super.logNbSides()} which are all equal`;
  }
}
Square.logDescription(); // 'I have 4 sides which are all equal'

在类字段声明中访问 super

¥Accessing super in class field declaration

super 也可以在类字段初始化期间访问。super 的引用取决于当前字段是实例字段还是静态字段。

¥super can also be accessed during class field initialization. The reference of super depends on whether the current field is an instance field or a static field.

js
class Base {
  static baseStaticField = 90;
  baseMethod() {
    return 10;
  }
}

class Extended extends Base {
  extendedField = super.baseMethod(); // 10
  static extendedStaticField = super.baseStaticField; // 90
}

请注意,实例字段是在实例上设置的,而不是在构造函数的 prototype 上设置的,因此你不能使用 super 来访问超类的实例字段。

¥Note that instance fields are set on the instance instead of the constructor's prototype, so you can't use super to access the instance field of a superclass.

js
class Base {
  baseField = 10;
}

class Extended extends Base {
  extendedField = super.baseField; // undefined
}

这里,extendedFieldundefined 而不是 10,因为 baseField 被定义为 Base 实例的自己的属性,而不是 Base.prototype。在这种情况下,super 只查找 Base.prototype 上的属性,因为那是 Extended.prototype 的 [[Prototype]]。

¥Here, extendedField is undefined instead of 10, because baseField is defined as an own property of the Base instance, instead of Base.prototype. super, in this context, only looks up properties on Base.prototype, because that's the [[Prototype]] of Extended.prototype.

删除超级属性会抛出错误

¥Deleting super properties will throw an error

你不能使用 delete 运算符super.propsuper[expr] 来删除父类的属性 - 它会抛出 ReferenceError

¥You cannot use the delete operator and super.prop or super[expr] to delete a parent class' property — it will throw a ReferenceError.

js
class Base {
  foo() {}
}
class Derived extends Base {
  delete() {
    delete super.foo; // this is bad
  }
}

new Derived().delete(); // ReferenceError: invalid delete involving 'super'.

在对象字面量中使用 super.prop

¥Using super.prop in object literals

Super 也可以用在 对象初始值设定项 表示法中。在此示例中,两个对象定义了一个方法。在第二个对象中,super 调用第一个对象的方法。这在 Object.setPrototypeOf() 的帮助下工作,我们可以将 obj2 的原型设置为 obj1,以便 super 能够在 obj1 上找到 method1

¥Super can also be used in the object initializer notation. In this example, two objects define a method. In the second object, super calls the first object's method. This works with the help of Object.setPrototypeOf() with which we are able to set the prototype of obj2 to obj1, so that super is able to find method1 on obj1.

js
const obj1 = {
  method1() {
    console.log("method 1");
  },
};

const obj2 = {
  method2() {
    super.method1();
  },
};

Object.setPrototypeOf(obj2, obj1);
obj2.method2(); // Logs "method 1"

读取 super.prop 的方法在绑定到其他对象时不会有不同的行为

¥Methods that read super.prop do not behave differently when bound to other objects

访问 super.x 的行为与 Reflect.get(Object.getPrototypeOf(objectLiteral), "x", this) 类似,这意味着始终在对象文字/类声明的原型上查找属性,并且取消绑定和重新绑定方法不会更改 super 的引用。

¥Accessing super.x behaves like Reflect.get(Object.getPrototypeOf(objectLiteral), "x", this), which means the property is always seeked on the object literal/class declaration's prototype, and unbinding and re-binding a method won't change the reference of super.

js
class Base {
  baseGetX() {
    return 1;
  }
}
class Extended extends Base {
  getX() {
    return super.baseGetX();
  }
}

const e = new Extended();
console.log(e.getX()); // 1
const { getX } = e;
console.log(getX()); // 1

同样的情况也发生在对象字面量中。

¥The same happens in object literals.

js
const parent1 = { prop: 1 };
const parent2 = { prop: 2 };

const child = {
  myParent() {
    console.log(super.prop);
  },
};

Object.setPrototypeOf(child, parent1);
child.myParent(); // Logs "1"

const myParent = child.myParent;
myParent(); // Still logs "1"

const anotherChild = { __proto__: parent2, myParent };
anotherChild.myParent(); // Still logs "1"

只有重置整个继承链才会改变 super 的引用。

¥Only resetting the entire inheritance chain will change the reference of super.

js
class Base {
  baseGetX() {
    return 1;
  }
  static staticBaseGetX() {
    return 3;
  }
}
class AnotherBase {
  baseGetX() {
    return 2;
  }
  static staticBaseGetX() {
    return 4;
  }
}
class Extended extends Base {
  getX() {
    return super.baseGetX();
  }
  static staticGetX() {
    return super.staticBaseGetX();
  }
}

const e = new Extended();
// Reset instance inheritance
Object.setPrototypeOf(Extended.prototype, AnotherBase.prototype);
console.log(e.getX()); // Logs "2" instead of "1", because the prototype chain has changed
console.log(Extended.staticGetX()); // Still logs "3", because we haven't modified the static part yet
// Reset static inheritance
Object.setPrototypeOf(Extended, AnotherBase);
console.log(Extended.staticGetX()); // Now logs "4"

从 super 调用方法

¥Calling methods from super

当将 super.prop 作为函数调用时,prop 函数内部的 this 值是当前的 this,而不是 super 指向的对象。例如,super.getName() 调用记录 "Extended",尽管代码看起来与 Base.getName() 等效。

¥When calling super.prop as a function, the this value inside the prop function is the current this, not the object that super points to. For example, the super.getName() call logs "Extended", despite the code looking like it's equivalent to Base.getName().

js
class Base {
  static getName() {
    console.log(this.name);
  }
}

class Extended extends Base {
  static getName() {
    super.getName();
  }
}

Extended.getName(); // Logs "Extended"

这在与 静态私有属性 互动时尤其重要。

¥This is especially important when interacting with static private properties.

设置 super.prop 来设置 this 的属性

¥Setting super.prop sets the property on this instead

设置 super 的属性(例如 super.x = 1)的行为与 Reflect.set(Object.getPrototypeOf(objectLiteral), "x", 1, this) 类似。这是将 super 简单理解为 "原型对象的引用" 的情况之一,因为它实际上将属性设置在 this 上。

¥Setting properties of super, such as super.x = 1, behaves like Reflect.set(Object.getPrototypeOf(objectLiteral), "x", 1, this). This is one of the cases where understanding super as simply "reference of the prototype object" falls short, because it actually sets the property on this instead.

js
class A {}
class B extends A {
  setX() {
    super.x = 1;
  }
}

const b = new B();
b.setX();
console.log(b); // B { x: 1 }
console.log(Object.hasOwn(b, "x")); // true

super.x = 1 将在 A.prototype 上查找 x 的属性描述符(并调用那里定义的 setter),但 this 值将设置为 this,在此上下文中为 b。当 targetreceiver 不同时,你可以阅读 Reflect.set 了解更多有关情况的详细信息。

¥super.x = 1 will look for the property descriptor of x on A.prototype (and invoke the setters defined there), but the this value will be set to this, which is b in this context. You can read Reflect.set for more details on the case when target and receiver differ.

这意味着虽然获取 super.prop 的方法通常不易受到 this 上下文中更改的影响,但设置 super.prop 的方法则不然。

¥This means that while methods that get super.prop are usually not susceptible to changes in the this context, those that set super.prop are.

js
/* Reusing same declarations as above */

const b2 = new B();
b2.setX.call(null); // TypeError: Cannot assign to read only property 'x' of object 'null'

然而,super.x = 1 仍然参考原型对象的属性描述符,这意味着你不能重写不可写的属性,并且将调用 setter。

¥However, super.x = 1 still consults the property descriptor of the prototype object, which means you cannot rewrite non-writable properties, and setters will be invoked.

js
class X {
  constructor() {
    // Create a non-writable property
    Object.defineProperty(this, "prop", {
      configurable: true,
      writable: false,
      value: 1,
    });
  }
}

class Y extends X {
  constructor() {
    super();
  }
  foo() {
    super.prop = 2; // Cannot overwrite the value.
  }
}

const y = new Y();
y.foo(); // TypeError: "prop" is read-only
console.log(y.prop); // 1

规范

Specification
ECMAScript Language Specification
# sec-super-keyword

¥Specifications

浏览器兼容性

BCD tables only load in the browser

¥Browser compatibility

也可以看看

¥See also