super

基线 广泛可用

此功能已得到完善,并在许多设备和浏览器版本上运行。它自以下时间起在所有浏览器中可用 2017 年 3 月.

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

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

试一试

语法

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

描述

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

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

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

在派生类(使用 extends)的 构造函数 体中,super 关键字可以作为“函数调用”(super(...args))出现,它必须在使用 this 关键字之前以及构造函数返回之前调用。它调用父类的构造函数并绑定父类的公共字段,之后派生类的构造函数可以进一步访问和修改 this

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

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

通过 super 设置属性时,该属性将设置为 this 而不是。

示例

在类中使用 super

此代码片段取自 类示例 (实时演示)。这里调用了 super() 以避免重复 RectangleSquare 之间共有的构造函数部分。

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。

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

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

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

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

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

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]]。

删除 super 属性将引发错误

您不能使用 delete 运算符super.propsuper[expr] 删除父类属性 - 它将引发 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

Super 也可以在 对象初始化器 符号中使用。在这个例子中,两个对象定义了一个方法。在第二个对象中,super 调用第一个对象的方法。这借助于 Object.setPrototypeOf() 来实现,我们可以使用它将 obj2 的原型设置为 obj1,以便 super 能够在 obj1 上找到 method1

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

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

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

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

访问 super.x 的行为类似于 Reflect.get(Object.getPrototypeOf(objectLiteral), "x", this),这意味着该属性始终在对象字面量/类声明的原型上查找,解绑和重新绑定方法不会更改 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

在对象字面量中也是如此。

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 的引用。

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 调用方法

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

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

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

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

这在与 静态私有属性 交互时尤为重要。

设置 super.prop 会将属性设置为 this 而不是

设置 super 的属性,如 super.x = 1,的行为类似于 Reflect.set(Object.getPrototypeOf(objectLiteral), "x", 1, this)。这是理解 super 仅仅是“原型对象的引用”不足的地方之一,因为它实际上将属性设置为 this 而不是。

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。您可以阅读 Reflect.set 以了解 targetreceiver 不同的情况。

这意味着,虽然获取 super.prop 的方法通常不受 this 上下文变化的影响,但设置 super.prop 的方法会受到影响。

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。

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

规范

规范
ECMAScript 语言规范
# sec-super-keyword

浏览器兼容性

BCD 表仅在浏览器中加载

另请参阅