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()
调用了父类Animal
的makeSound
办法,然后在子类中添加了额定的行为。这种运用办法能够很好地保留父类的行为并在其基础上进行扩展。
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!');
};
在这个等效的例子中,咱们运用结构函数Animal
和Dog
,并经过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
来获取私有成员的值。