在 JavaScript 中,有多种实现继承的方法,最常用的有原型链继承、构造函数继承、组合继承和 class
继承(ES6)。下面以 ES6 的 class
继承为例,展示如何实现继承:
示例:
// 父类
class Animal {constructor(name) {this.name = name;}speak() {console.log(`${this.name} makes a noise.`);}
}// 子类继承父类
class Dog extends Animal {constructor(name, breed) {super(name); // 调用父类构造函数this.breed = breed;}speak() {console.log(`${this.name} barks.`);}
}const dog = new Dog('Buddy', 'Golden Retriever');
dog.speak(); // 输出: Buddy barks.
解释:
Animal
是父类,它有一个构造函数和一个方法speak()
。Dog
是子类,使用了extends
关键字继承Animal
。子类的构造函数使用super()
调用了父类的构造函数。- 子类可以继承父类的方法,也可以重写父类方法(如
speak()
)。
这是 ES6 中推荐的继承方式,简洁且符合面向对象编程的设计。
在 JavaScript 中,实现继承的方式有多种,下面分别给出几种常用继承方式的代码示例。
1. 原型链继承
通过将子类的原型指向父类的实例来实现继承。
// 父类构造函数
function Animal(name) {this.name = name;
}Animal.prototype.speak = function() {console.log(this.name + ' makes a noise.');
};// 子类构造函数
function Dog(name) {this.name = name;
}// 原型链继承
Dog.prototype = new Animal();const dog = new Dog('Buddy');
dog.speak(); // 输出: Buddy makes a noise.
缺点:所有子类实例共享父类实例的属性,如果修改父类引用类型的属性会影响所有子类实例。
2. 构造函数继承
通过在子类构造函数中调用父类构造函数,来避免共享父类的引用属性。
// 父类构造函数
function Animal(name) {this.name = name;
}// 子类构造函数
function Dog(name, breed) {Animal.call(this, name); // 调用父类构造函数this.breed = breed;
}const dog = new Dog('Buddy', 'Golden Retriever');
console.log(dog.name); // 输出: Buddy
console.log(dog.breed); // 输出: Golden Retriever
优点:可以避免原型链继承中的共享引用问题。
3. 组合继承
组合了原型链和构造函数继承的优点,父类的属性通过构造函数继承,方法通过原型链继承。
// 父类构造函数
function Animal(name) {this.name = name;
}Animal.prototype.speak = function() {console.log(this.name + ' makes a noise.');
};// 子类构造函数
function Dog(name, breed) {Animal.call(this, name); // 继承属性this.breed = breed;
}// 继承父类方法
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;const dog = new Dog('Buddy', 'Golden Retriever');
dog.speak(); // 输出: Buddy makes a noise.
优点:能够避免原型链的引用共享问题,并且继承了父类的属性和方法。
4. ES6 class
继承
使用 class
语法更加简洁,且语义更清晰。
// 父类
class Animal {constructor(name) {this.name = name;}speak() {console.log(`${this.name} makes a noise.`);}
}// 子类
class Dog extends Animal {constructor(name, breed) {super(name); // 调用父类构造函数this.breed = breed;}speak() {console.log(`${this.name} barks.`);}
}const dog = new Dog('Buddy', 'Golden Retriever');
dog.speak(); // 输出: Buddy barks.
优点:语法简洁,且 ES6 class
是语法糖,底层仍是基于原型实现的。
5. 寄生组合式继承
这是组合继承的优化版本,避免了调用两次父类构造函数的问题。
function Animal(name) {this.name = name;
}Animal.prototype.speak = function() {console.log(this.name + ' makes a noise.');
};function Dog(name, breed) {Animal.call(this, name); // 调用父类构造函数,继承属性this.breed = breed;
}// 优化继承
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;const dog = new Dog('Buddy', 'Golden Retriever');
dog.speak(); // 输出: Buddy makes a noise.
优点:避免了调用两次父类构造函数的问题,性能更优。
总结:
- 原型链继承:简单但存在引用共享问题。
- 构造函数继承:可以避免共享引用类型,但无法继承方法。
- 组合继承:结合原型链和构造函数继承的优点,但调用了两次父类构造函数。
- ES6
class
继承:语法简洁,是基于原型链的语法糖。 - 寄生组合式继承:优化版的组合继承,性能更好。