阿里云主机折上折
  • 微信号
Current Site:Index > The usage of the super keyword

The usage of the super keyword

Author:Chuan Chen 阅读数:62892人阅读 分类: JavaScript

Basic Concepts of the super Keyword

The super keyword was introduced in ECMAScript 6 and is primarily used in subclasses to call the constructor or methods of the parent class. It provides two usage modes: as a function call and as an object. When used as a function call, it can only be used in the constructor of a subclass; when used as an object, it can be used in any method of the subclass.

class Parent {
  constructor(name) {
    this.name = name;
  }
  
  sayHello() {
    return `Hello, ${this.name}`;
  }
}

class Child extends Parent {
  constructor(name, age) {
    super(name);  // Called as a function
    this.age = age;
  }
  
  greet() {
    return `${super.sayHello()}! You are ${this.age} years old.`;  // Used as an object
  }
}

const child = new Child('Alice', 10);
console.log(child.greet());  // Output: Hello, Alice! You are 10 years old.

Using super in Constructors

In the constructor of a subclass, super must be called before using the this keyword; otherwise, a ReferenceError will be thrown. This is because the this object of the subclass must first be shaped by the parent class's constructor, inheriting the same instance properties and methods as the parent class, before being further processed to add the subclass's own instance properties and methods.

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

class Rabbit extends Animal {
  constructor(name, earLength) {
    // super() must be called first
    super(name);
    this.earLength = earLength;
  }
  
  // Incorrect usage, will throw a ReferenceError
  /*
  constructor(name, earLength) {
    this.earLength = earLength;  // Error!
    super(name);
  }
  */
}

const rabbit = new Rabbit('White Rabbit', 10);
console.log(rabbit.name);  // White Rabbit
console.log(rabbit.earLength);  // 10

Calling Parent Class Methods with super

When super is used as an object, it can call methods of the parent class. In regular methods, super refers to the parent class's prototype object; in static methods, super refers to the parent class itself.

class Person {
  constructor(name) {
    this.name = name;
  }
  
  introduce() {
    return `My name is ${this.name}`;
  }
}

class Student extends Person {
  constructor(name, grade) {
    super(name);
    this.grade = grade;
  }
  
  introduce() {
    return `${super.introduce()} and I'm in grade ${this.grade}`;
  }
  
  static sayHello() {
    return 'Hello from Student class';
  }
}

class CollegeStudent extends Student {
  static sayHello() {
    return `${super.sayHello()} (now in college)`;
  }
}

const student = new Student('Bob', 5);
console.log(student.introduce());  // My name is Bob and I'm in grade 5

console.log(CollegeStudent.sayHello());  // Hello from Student class (now in college)

super and Static Methods

In static methods, super refers to the parent class rather than the parent class's prototype object. This allows subclasses to call static methods of the parent class.

class Base {
  static getMessage() {
    return 'Base static message';
  }
  
  getInstanceMessage() {
    return 'Base instance message';
  }
}

class Derived extends Base {
  static getMessage() {
    return `${super.getMessage()} + Derived static message`;
  }
  
  getInstanceMessage() {
    return `${super.getInstanceMessage()} + Derived instance message`;
  }
}

console.log(Derived.getMessage());  // Base static message + Derived static message

const derived = new Derived();
console.log(derived.getInstanceMessage());  // Base instance message + Derived instance message

Using super in Object Literals

ES6 allows the use of the super keyword in object literals, but it must be used with method shorthand syntax.

const parent = {
  greet() {
    return 'Hello from parent';
  }
};

const child = {
  __proto__: parent,
  greet() {
    return `${super.greet()} and hello from child`;
  }
};

console.log(child.greet());  // Hello from parent and hello from child

Binding of super

The binding of super is static and is determined when the method is defined, not when it is called. This means that even if the method is assigned to another object, super still refers to the original parent class.

class A {
  say() {
    return 'A';
  }
}

class B extends A {
  say() {
    return `${super.say()}B`;
  }
  
  getSayMethod() {
    return this.say;
  }
}

const b = new B();
console.log(b.say());  // AB

const obj = {
  say() {
    return 'obj';
  }
};

// Assigning the method to another object
const say = b.getSayMethod();
console.log(say.call(obj));  // Still AB, not objB

super and Arrow Functions

Arrow functions do not have their own super binding; they inherit the super binding from the surrounding context. This allows the use of super in arrow functions to avoid this binding issues.

class Parent {
  greet() {
    return 'Parent greeting';
  }
}

class Child extends Parent {
  constructor() {
    super();
    this.greet = () => super.greet();
  }
  
  // Arrow function in a regular method
  getGreeter() {
    return () => super.greet();
  }
}

const child = new Child();
console.log(child.greet());  // Parent greeting

const greeter = child.getGreeter();
console.log(greeter());  // Parent greeting

Behavior of super in Multiple Inheritance

JavaScript does not natively support multiple inheritance, but it can be simulated using mixins. In such cases, the behavior of super requires special attention.

class A {
  method() {
    return 'A';
  }
}

const B = {
  method() {
    return `${super.method()}B`;
  }
};

const C = {
  method() {
    return `${super.method()}C`;
  }
};

// Setting up the prototype chain
Object.setPrototypeOf(B, A.prototype);
Object.setPrototypeOf(C, B);

class D extends A {
  method() {
    return `${super.method()}D`;
  }
}

// Mixing C into D
Object.assign(D.prototype, C);

const d = new D();
console.log(d.method());  // ACD

super and Proxy

When a class is wrapped with a Proxy, the behavior of super may be affected because the Proxy can intercept property access.

class Base {
  value = 10;
  
  getValue() {
    return this.value;
  }
}

const handler = {
  get(target, prop, receiver) {
    if (prop === 'value') {
      return 20;
    }
    return Reflect.get(target, prop, receiver);
  }
};

class Derived extends Base {
  getValue() {
    return super.getValue();
  }
}

const proxy = new Proxy(new Derived(), handler);
console.log(proxy.getValue());  // 20, because the Proxy intercepts access to 'value'

Using super in Asynchronous Methods

super can also be used in asynchronous methods, with behavior consistent with synchronous methods.

class AsyncBase {
  async fetchData() {
    return new Promise(resolve => {
      setTimeout(() => resolve('Base data'), 100);
    });
  }
}

class AsyncDerived extends AsyncBase {
  async fetchData() {
    const baseData = await super.fetchData();
    return `${baseData} + Derived data`;
  }
}

(async () => {
  const derived = new AsyncDerived();
  console.log(await derived.fetchData());  // Base data + Derived data
})();

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

上一篇:类的继承extends

下一篇:getter和setter方法

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.