herança de classe
A classe pode
extends
implementar 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 Student
a subclasse, que herda todas as propriedades e métodos da classe por meio de palavras- extends
chave . Point
Mas como nenhum código é implantado, as duas classes são exatamente iguais, o que é uma duplicata de uma Point
classe.
Abaixo, Student
adicionamos 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()
super
super
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 this
objeto 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 this
objeto.
class Point { /* ... */ }
class Student extends Point {
constructor() {
}
}
let cp = new Student(); // ReferenceError
No código acima,
Student
a classe pai é herdadaPoint
, mas seu construtor não é chamadosuper()
, 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 osuper()
método primeiro, porque esta etapa irá gerar umthis
objeto 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 chamadathis
, 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 somentesuper()
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()
this
super()
Se a subclasse não definir um
constructor()
método, este método será adicionado por padrão e será chamado dentro dosuper()
. Ou seja, qualquer subclasse possuiconstructor()
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ânciaColorPoint
dePoint
ambas 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()
éA
o método estático da classe,B
herdadoA
e também oA
mé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
super
Essa palavra-chave pode ser usada tanto como função quanto como objeto. Seu uso é completamente diferente em ambos os casos.O primeiro caso,
super
quando chamado como uma função, representa o construtor da classe pai. ES6 requer que o construtor de uma subclasse execute umasuper
função uma vez.
class A {}
class B extends A {
constructor() {
super();
}
}
No código acima,
B
entre os construtores da subclasse, o construtor dasuper()
classe pai é chamado. Isso é necessário, caso contrário, o mecanismo JavaScript gerará um erro.Observe que
super
embora represente o construtor da classe pai , ele retorna uma instância daA
subclasse , ou seja, o interno se refere à instância, portanto é equivalente aqui .B
super
this
B
super()
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.target
aponta para a função em execução no momento. Como você pode ver, emsuper()
tempo de execução, ele aponta para o construtor da subclasseB
, não para o construtor da classe paiA
. Ou seja,super()
o ponto internothis
éB
.Quando usado como uma função, só
super()
pode ser usado no construtorconstructor
da e um erro será relatado em outros lugares.
class A {}
class B extends A {
m() {
super(); // 报错
}
}
No código acima, quando
super()
usado noB
método da classem
, causará um erro de sintaxe.No segundo caso,
super
como 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()
usadasuper
como um objeto. Neste momento,super
no método comum, aponte paraA.prototype
, entãosuper.p()
é equivalente aA.prototype.p()
.Deve-se notar aqui que, por
super
apontar para o objeto protótipo da classe pai, os métodos ou propriedades definidos na instância da classe pai não podem sersuper
chamados.
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
p
da classe pai , portanto, não pode ser referenciado.A
super.p
Se o atributo estiver definido no objeto protótipo da classe pai,
super
ele 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
é definidaA.prototype
acima, entãosuper.x
seu valor pode ser obtido.ES6 estipula que quando
super
o método da classe pai é chamado no método ordinário da subclasse, a parte interna do métodothis
aponta 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 yesA.prototype.print()
, a instânciaA.prototype.print()
internathis
aponta para a subclass , resultando na saída de yes , not . Ou seja, o que realmente executa é .B
2
1
super.print.call(this)
Como
this
aponta para uma instância de subclasse, se umsuper
atributo receber um valor, o atributo atribuído se tornará o atributo da instância de subclasse.super
this
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.x
atribuição é3
, neste momento, é equivalente athis.x
atribuir a3
. E quando lersuper.x
, ler éA.prototype.x
, então retorneundefined
.Se for
super
usado como um objeto em um método estático, elesuper
apontará 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,
super
o método estático aponta para a classe pai e o método normal aponta para o objeto protótipo da classe pai.
super
Além disso, ao chamar o método da classe pai no método estático da subclasse, o método internothis
aponta 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.m
dentro do método estático, elesuper.print
aponta para o método estático da classe pai.this
O ponteiro dentro desse método éB
, nãoB
a 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)
ésuper
impossí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 indicadosuper
, 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 quesuper
é um objeto, portanto, nenhum erro será relatado. Ao mesmo tempo, comosuper
a instânciathis
apontada é feitaB
, elasuper.valueOf()
retorna umaB
instância de .
super
Finalmente, 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]