[ES6] - herança de classe

 herança de classe

A classe pode extendsimplementar herança por meio de palavras-chave, permitindo que as subclasses herdem as propriedades e métodos da classe pai. A maneira de escrever estende é muito mais clara e conveniente do que a herança da cadeia de protótipos do ES5.

 

// 继承Person类中的sayHi方法
class Person {
    sayHi(){
        console.log("hello");
    }
}

class Chinese extends Person {}


// 继承Person类中的属性和方法
class Person {
    constructor(name, age, gender){
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    sayHi(){
        console.log("hello");
    }
}

// 子类
class Chinese extends Person {
    constructor(name, age, gender, skin){
        // 在子类中,必须在constructor函数中,首先调用super()
        super(name, age, gender);	// 相当于Person.call(this,name,age,gender)
        // 调用super之后才可以去写其他代码
        this.skin = skin;
    }
}
let xm = new Chinese("xm", 20, "male", "黄");
console.log(xm);
xm.sayHi();
class Point {
}

class Student extends Point {
}

No exemplo acima, Pointé a classe pai e Studenta subclasse, que herda todas as propriedades e métodos da classe por meio de palavras- extendschave . PointMas como nenhum código é implantado, as duas classes são exatamente iguais, o que é uma duplicata de uma Pointclasse.

Abaixo, Studentadicionamos código dentro.

class Point { /* ... */ }
​
class Student extends Point {
  constructor(x, y, color) {
    super(x, y); // 调用父类的constructor(x, y)
    this.color = color;
  }
​
  toString() {
    return this.color + ' ' + super.toString(); // 调用父类的toString()
  }
}

No exemplo acima, as palavras- chave aparecem em constructor()métodos e métodos. Aqui, o construtor da classe pai é representado, que é usado para criar um objeto de instância da classe pai.toString()supersuper

O ES6 estipula que as subclasses devem ser chamadas em constructor()métodos super(), caso contrário, um erro será relatado. Isso ocorre porque o thisobjeto da subclasse deve primeiro ser moldado pelo construtor da superclasse para obter os mesmos atributos e métodos de instância que a superclasse e, em seguida, processá-lo para adicionar os próprios atributos e métodos de instância da subclasse. Se você não chamar super()o método, a subclasse não terá seu próprio thisobjeto.

class Point { /* ... */ }
​
class Student extends Point {
  constructor() {
  }
}
​
let cp = new Student(); // ReferenceError

No código acima, Studenta classe pai é herdada Point, mas seu construtor não é chamado super(), resultando em um erro ao criar uma nova instância.

Por que o construtor da subclasse deve ser chamado super()? A razão é que o mecanismo de herança do ES6 é completamente diferente do ES5. O mecanismo de herança do ES5 é primeiro criar um objeto de instância de uma subclasse independente e, em seguida, adicionar o método da classe pai a esse objeto, ou seja, "instância primeiro, herança depois". O mecanismo de herança do ES6 é primeiro adicionar as propriedades e métodos da classe pai a um objeto vazio e, em seguida, usar o objeto como uma instância da subclasse, ou seja, "herança primeiro, instância por último". É por isso que a herança ES6 tem que chamar o super()método primeiro, porque esta etapa irá gerar um thisobjeto que herda a classe pai, e a classe pai não pode ser herdada sem esta etapa.

Observe que isso significa que quando uma nova instância de uma subclasse é criada, o construtor da superclasse deve ser executado uma vez.

class Foo {
  constructor() {
    console.log(1);
  }
}
​
class Bar extends Foo {
  constructor() {
    super();
    console.log(2);
  }
}
​
const bar = new Bar();
// 1
// 2

No exemplo acima, quando uma nova instância da subclasse Bar é criada, 1 e 2 serão gerados. A razão é que quando o construtor da subclasse é chamado super(), o construtor da classe pai será executado uma vez.

Outro ponto a ser observado é que no construtor da subclasse, a palavra-chave super()só pode ser utilizada após a chamada this, caso contrário será reportado um erro. Isso ocorre porque a construção de uma instância de subclasse deve completar a herança da classe pai primeiro, e somente super()métodos podem permitir que a instância da subclasse herde a classe pai.

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
}
​
class ColorPoint extends Point {
  constructor(x, y, color) {
    this.color = color; // ReferenceError
    super(x, y);
    this.color = color; // 正确
  }
}

No código acima, a palavra- chave é usada antes que o método da subclasse seja constructor()chamado e o resultado é um erro, mas está correto após ser colocado.super()thissuper()

Se a subclasse não definir um constructor()método, este método será adicionado por padrão e será chamado dentro do super(). Ou seja, qualquer subclasse possui constructor()métodos, definidos explicitamente ou não.

class ColorPoint extends Point {
}
​
// 等同于
class ColorPoint extends Point {
  constructor(...args) {
    super(...args);
  }
}

Com a definição da subclasse, você pode gerar uma instância da subclasse.

let cp = new ColorPoint(25, 8, 'green');
​
cp instanceof ColorPoint // true
cp instanceof Point // true

No exemplo acima, o objeto de instância cpé uma instância ColorPointde Pointambas as classes ao mesmo tempo, que é exatamente o mesmo comportamento do ES5.

Exceto para propriedades privadas, todas as propriedades e métodos da classe pai serão herdados pela subclasse, incluindo métodos estáticos.

class A {
  static hello() {
    console.log('hello world');
  }
}
​
class B extends A {
}
​
B.hello()  // hello world

No código acima, hello()é Ao método estático da classe, Bherdado Ae também o Amétodo estático herdado.

As subclasses não podem herdar as propriedades privadas da classe pai, ou seja, as propriedades privadas só podem ser usadas na classe em que estão definidas.

class Foo {
  #p = 1;
  #m() {
    console.log('hello');
  }
}
​
class Bar extends Foo {
  constructor() {
    super();
    console.log(this.#p); // 报错
    this.#m(); // 报错
  }
}

No exemplo acima, se a subclasse Bar chamar a propriedade privada ou o método privado da classe pai Foo, um erro será relatado.

Se a classe pai define métodos de leitura e gravação para propriedades privadas, as subclasses podem usar esses métodos para ler e gravar propriedades privadas.

class Foo {
  #p = 1;
  getP() {
    return this.#p;
  }
}
​
class Bar extends Foo {
  constructor() {
    super();
    console.log(this.getP()); // 1
  }
}

No exemplo acima, getP()é o método usado pela classe pai para ler as propriedades privadas.Através deste método, a subclasse pode ler as propriedades privadas da classe pai.

super palavra-chave 

superEssa palavra-chave pode ser usada tanto como função quanto como objeto. Seu uso é completamente diferente em ambos os casos.

O primeiro caso, superquando chamado como uma função, representa o construtor da classe pai. ES6 requer que o construtor de uma subclasse execute uma superfunção uma vez.

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

No código acima, Bentre os construtores da subclasse, o construtor da super()classe pai é chamado. Isso é necessário, caso contrário, o mecanismo JavaScript gerará um erro.

Observe que superembora represente o construtor da classe pai , ele retorna uma instância da Asubclasse , ou seja, o interno se refere à instância, portanto é equivalente aqui .BsuperthisBsuper()A.prototype.constructor.call(this)

class A {
  constructor() {
    console.log(new.target.name);
  }
}
class B extends A {
  constructor() {
    super();
  }
}
new A() // A
new B() // B

No código acima, ele new.targetaponta para a função em execução no momento. Como você pode ver, em super()tempo de execução, ele aponta para o construtor da subclasse B, não para o construtor da classe pai A. Ou seja, super()o ponto interno thisé B.

Quando usado como uma função, só super()pode ser usado no construtor constructorda e um erro será relatado em outros lugares.

class A {}
​
class B extends A {
  m() {
    super(); // 报错
  }
}

No código acima, quando super()usado no Bmétodo da classe m, causará um erro de sintaxe.

No segundo caso, supercomo um objeto, em métodos comuns, aponta para o objeto protótipo da classe pai; em métodos estáticos, aponta para a classe pai.

class A {
  p() {
    return 2;
  }
}
​
class B extends A {
  constructor() {
    super();
    console.log(super.p()); // 2
  }
}
​
let b = new B();

No código acima, a subclasse Bé super.p()usada supercomo um objeto. Neste momento, superno método comum, aponte para A.prototype, então super.p()é equivalente a A.prototype.p().

Deve-se notar aqui que, por superapontar para o objeto protótipo da classe pai, os métodos ou propriedades definidos na instância da classe pai não podem ser superchamados.

class A {
  constructor() {
    this.p = 2;
  }
}
​
class B extends A {
  get m() {
    return super.p;
  }
}
​
let b = new B();
b.m // undefined

No código acima, é um atributo da instância pda classe pai , portanto, não pode ser referenciado.Asuper.p

Se o atributo estiver definido no objeto protótipo da classe pai, superele poderá ser obtido.

class A {}
A.prototype.x = 2;
​
class B extends A {
  constructor() {
    super();
    console.log(super.x) // 2
  }
}
​
let b = new B();

No código acima, a propriedade xé definida A.prototypeacima, então super.xseu valor pode ser obtido.

ES6 estipula que quando supero método da classe pai é chamado no método ordinário da subclasse, a parte interna do método thisaponta para a instância atual da subclasse.

class A {
  constructor() {
    this.x = 1;
  }
  print() {
    console.log(this.x);
  }
}
​
class B extends A {
  constructor() {
    super();
    this.x = 2;
  }
  m() {
    super.print();
  }
}
​
let b = new B();
b.m() // 2

No código acima, super.print()embora a chamada seja yes A.prototype.print(), a instância A.prototype.print()interna thisaponta para a subclass , resultando na saída de yes , not . Ou seja, o que realmente executa é .B21super.print.call(this)

Como thisaponta para uma instância de subclasse, se um superatributo receber um valor, o atributo atribuído se tornará o atributo da instância de subclasse.superthis

class A {
  constructor() {
    this.x = 1;
  }
}
​
class B extends A {
  constructor() {
    super();
    this.x = 2;
    super.x = 3;
    console.log(super.x); // undefined
    console.log(this.x); // 3
  }
}
​
let b = new B();

No código acima, a super.xatribuição é 3, neste momento, é equivalente a this.xatribuir a 3. E quando ler super.x, ler é A.prototype.x, então retorne undefined.

Se for superusado como um objeto em um método estático, ele superapontará para a classe pai em vez do objeto protótipo da classe pai.

class Parent {
  static myMethod(msg) {
    console.log('static', msg);
  }
​
  myMethod(msg) {
    console.log('instance', msg);
  }
}
​
class Child extends Parent {
  static myMethod(msg) {
    super.myMethod(msg);
  }
​
  myMethod(msg) {
    super.myMethod(msg);
  }
}
​
Child.myMethod(1); // static 1
​
var child = new Child();
child.myMethod(2); // instance 2

No código acima, supero método estático aponta para a classe pai e o método normal aponta para o objeto protótipo da classe pai.

superAlém disso, ao chamar o método da classe pai no método estático da subclasse, o método interno thisaponta para a subclasse atual, não para a instância da subclasse.

class A {
  constructor() {
    this.x = 1;
  }
  static print() {
    console.log(this.x);
  }
}
​
class B extends A {
  constructor() {
    super();
    this.x = 2;
  }
  static m() {
    super.print();
  }
}
​
B.x = 3;
B.m() // 3

No código acima, B.mdentro do método estático, ele super.printaponta para o método estático da classe pai. thisO ponteiro dentro desse método é B, não Ba instância.

Observe que ao usá super-lo, você deve especificar explicitamente se deseja usá-lo como uma função ou como um objeto, caso contrário, um erro será relatado.

class A {}
​
class B extends A {
  constructor() {
    super();
    console.log(super); // 报错
  }
}

No código acima, console.log(super)é superimpossível dizer se ele é usado como uma função ou como um objeto, portanto, um erro será relatado quando o mecanismo JavaScript analisar o código. Neste momento, se o tipo de dados puder ser claramente indicado super, nenhum erro será relatado.

class A {}
​
class B extends A {
  constructor() {
    super();
    console.log(super.valueOf() instanceof B); // true
  }
}
​
let b = new B();

No código acima, super.valueOf()indica que superé um objeto, portanto, nenhum erro será relatado. Ao mesmo tempo, como supera instância thisapontada é feita B, ela super.valueOf()retorna uma Binstância de .

superFinalmente, como os objetos sempre herdam de outros objetos, você pode usar palavras- chave em qualquer objeto .

var obj = {
  toString() {
    return "MyObject: " + super.toString();
  }
};
​
obj.toString(); // MyObject: [object Object]

Acho que você gosta

Origin blog.csdn.net/m0_55960697/article/details/124086268
Recomendado
Clasificación