JavaScript中的类承继是面向目标编程的一个核心概念,它允许一个类(子类)承继另一个类(父类)的特点和办法。本文将深化探讨JavaScript类的承继机制,包括根本承继、结构函数的履行次序、办法的覆写、super关键字的妙用以及ES6中class关键字的完成原理。

1. 根本的类承继

在JavaScript中,咱们能够运用extends关键字来创立一个子类,并承继父类的特点和办法。

class Animal {
  constructor(name) {
    this.name = name;
  }
  eat() {
    console.log(`${this.name} is eating.`);
  }
}
class Dog extends Animal {
  bark() {
    console.log(`${this.name} is barking.`);
  }
}
const myDog = new Dog('Buddy');
myDog.eat(); // 输出 "Buddy is eating."
myDog.bark(); // 输出 "Buddy is barking."

在上述示例中,Dog类承继了Animal类,经过extends关键字建立了父子关系。这种根本承继机制让子类具有了父类的特点和办法。

2. 结构函数的履行次序

了解结构函数的履行次序对于深化了解承继机制非常重要。在一个承继关系中,子类的结构函数会在实例化时首先履行,然后调用父类的结构函数。

class Animal {
  constructor(name) {
    this.name = name;
    console.log(`Animal constructor executed for ${this.name}`);
  }
  eat() {
    console.log(`${this.name} is eating.`);
  }
}
class Dog extends Animal {
  constructor(name, breed) {
    super(name); // 调用父类结构函数
    this.breed = breed;
    console.log(`Dog constructor executed for ${this.name}`);
  }
  bark() {
    console.log(`${this.name} is barking.`);
  }
}
const myDog = new Dog('Buddy', 'Labrador');
// 输出:
// Animal constructor executed for Buddy
// Dog constructor executed for Buddy

在上述示例中,super(name)句子调用了父类Animal的结构函数,保证父类的初始化作业得以完成。结构函数的履行次序遵从类的承继链,从父类到子类。

3. 办法的覆写

子类能够对父类的办法进行覆写,即在子类中从头界说相同称号的办法。这允许子类在承继的基础上修改或扩展父类的行为。

class Animal {
  constructor(name) {
    this.name = name;
  }
  makeSound() {
    console.log('Generic animal sound');
  }
}
class Dog extends Animal {
  makeSound() {
    console.log('Woof! Woof!');
  }
}
const myDog = new Dog('Buddy');
myDog.makeSound(); // 输出 "Woof! Woof!"

在上述示例中,Dog类覆写了makeSound办法,使得子类具有了自己的完成。调用myDog.makeSound()时,将履行子类Dog中的办法。

4. super关键字的妙用

super关键字不仅用于调用父类的结构函数,还能够在子类办法中经过它来调用父类的同名办法。

class Animal {
  constructor(name) {
    this.name = name;
  }
  makeSound() {
    console.log('Generic animal sound');
  }
}
class Dog extends Animal {
  makeSound() {
    super.makeSound(); // 调用父类的makeSound办法
    console.log('Woof! Woof!');
  }
}
const myDog = new Dog('Buddy');
myDog.makeSound();
// 输出:
// Generic animal sound
// Woof! Woof!

在上述示例中,super.makeSound()调用了父类AnimalmakeSound办法,然后在子类中添加了额定的行为。这种运用办法能够很好地保留父类的行为并在其基础上进行扩展。

5. 承继与原型链

在JavaScript中,类承继的完成依赖于原型链。当创立一个子类时,子类的原型目标将链接到父类的原型目标上。这意味着子类实例能够拜访父类原型上的办法和特点。

class Animal {
  constructor(name) {
    this.name = name;
  }
  makeSound() {
    console.log('Generic animal sound');
  }
}
class Dog extends Animal {
  bark() {
    console.log('Woof! Woof!');
  }
}
const myDog = new Dog('Buddy');
myDog.makeSound(); // 输出 "Generic animal sound"

在上述示例中,myDog实例能够调用makeSound办法,虽然这个办法是界说在父类Animal的原型上的。这是因为子类Dog经过原型链承继了父类的办法。

6. ES6中class关键字的完成原理

虽然ES6引入了class关键字来更方便地创立类,但其本质仍然是基于JavaScript的原型链完成的。class关键字只是语法糖,它更清晰地表达了原型链承继的概念。

class Animal {
  constructor(name) {
    this.name = name;
  }
  makeSound() {
    console.log('Generic animal sound');
  }
}
class Dog extends Animal```javascript
{
  bark() {
    console.log('Woof! Woof!');
  }
}

上述代码等效于运用结构函数和原型链的办法界说类:

function Animal(name) {
  this.name = name;
}
Animal.prototype.makeSound = function () {
  console.log('Generic animal sound');
};
function Dog(name) {
  Animal.call(this, name);
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function () {
  console.log('Woof! Woof!');
};

在这个等效的例子中,咱们运用结构函数AnimalDog,并经过Object.create来设置Dog的原型为Animal的原型。这种办法与运用class关键字创立的类具有相同的承继作用。

7. 多重承继与混入

JavaScript自身不支持多重承继,即一个类同时承继多个父类。但是,能够经过混入(Mixin)的办法完成类似的作用。

// 界说一个能够混入的特性
const SoundMixin = {
  makeSound() {
    console.log('Making a sound');
  }
};
// 运用混入创立一个新类
class Animal {
  constructor(name) {
    this.name = name;
  }
}
// 将特性混入到类中
Object.assign(Animal.prototype, SoundMixin);
const myAnimal = new Animal('Mystery');
myAnimal.makeSound(); // 输出 "Making a sound"

在上述示例中,咱们界说了一个SoundMixin特性,它包括一个makeSound办法。然后,经过Object.assign将这个特性混入到Animal类中,使得Animal类具有了makeSound办法。

8. 运用Symbol完成私有成员

在类中,有时需求界说一些私有成员,以避免外部直接拜访。在ES6之前,通常运用命名约好(如在特点称号前加下划线)来模仿私有性。但是,在ES6中,能够运用Symbol类型来完成真正的私有成员。

const _name = Symbol('name');
class Animal {
  constructor(name) {
    this[_name] = name;
  }
  getName() {
    return this[_name];
  }
}
const myAnimal = new Animal('Leo');
console.log(myAnimal.getName()); // 输出 "Leo"
console.log(myAnimal._name); // 输出 undefined

在上述示例中,_name是一个Symbol类型的私有成员,它在类的结构函数中被赋值。这样,外部无法直接拜访_name,只能经过类中供给的公共办法getName来获取私有成员的值。