浅谈JavaScript面向对象 - 继承
什么是继承
在面向对象编程中,继承是指一个对象直接使用另一个对象的属性和方法的能力。被继承的对象称为父类或超类,继承它的对象称为子类或派生类。子类可以继承父类的所有公共方法和属性,同时还可以根据需求添加新的属性或方法。
JavaScript中的继承是基于原型(Prototype)实现的,每个对象都可以拥有原型,并继承从原型中继承来的属性和方法。
继承的实现方法
原型链继承
原型链继承是指让子类的原型指向父类的实例,从而实现继承。
// 定义一个Animal类
function Animal(name) {
this.name = name
}
// Animal类的实例方法
Animal.prototype.sayName = function() {
console.log(`My name is ${this.name}`)
}
// 定义一个Dog类,继承Animal类
function Dog(name, age) {
this.age = age
}
// Dog类的原型指向Animal类的实例
Dog.prototype = new Animal()
// Dog类的实例方法
Dog.prototype.sayAge = function() {
console.log(`My age is ${this.age}`)
}
// 创建一个Dog的实例,并调用它的方法
const dog = new Dog('旺财', 2)
dog.sayName() // My name is 旺财
dog.sayAge() // My age is 2
这种方式实现起来简单,但存在一个问题:如果父类的实例属性是引用类型,那么子类实例共享这个引用属性。比如:
function Animal() {
this.colors = ['黑色', '白色', '棕色']
}
function Dog() {}
Dog.prototype = new Animal()
const dog1 = new Dog()
const dog2 = new Dog()
// dog1和dog2的colors属性共享了同一个数组对象
dog1.colors.push('灰色')
console.log(dog2.colors) // ['黑色', '白色', '棕色', '灰色']
借用构造函数继承
通过调用父类的构造函数,实现子类继承父类的构造函数中的属性。这种方式可以解决上面提到的共享引用属性的问题。
// 定义一个Animal类
function Animal(name) {
this.name = name
this.colors = ['黑色', '白色', '棕色']
}
// 定义一个Dog类,继承Animal类
function Dog(name, age) {
Animal.call(this, name) // 调用Animal类的构造函数
this.age = age
}
// 创建一个Dog的实例,并调用它的方法
const dog = new Dog('大黄', 2)
console.log(dog.name) // 大黄
console.log(dog.age) // 2
dog.colors.push('灰色')
console.log(dog.colors) // ['黑色', '白色', '棕色', '灰色']
const dog2 = new Dog('二狗', 3)
console.log(dog2.colors) // ['黑色', '白色', '棕色']
这种方式的缺点是父类原型中的方法无法被子类继承。
组合式继承
组合式继承结合了原型链继承和借用构造函数继承的优点,在子类的构造函数中调用父类的构造函数,同时将子类的原型指向父类的实例。
// 定义一个Animal类
function Animal(name) {
this.name = name
this.colors = ['黑色', '白色', '棕色']
}
// Animal类的实例方法
Animal.prototype.sayName = function() {
console.log(`My name is ${this.name}`)
}
// 定义一个Dog类,继承Animal类
function Dog(name, age) {
Animal.call(this, name) // 借用父类的构造函数
this.age = age
}
// Dog的原型指向Animal类的实例
Dog.prototype = new Animal()
// Dog类的实例方法
Dog.prototype.sayAge = function() {
console.log(`My age is ${this.age}`)
}
// 创建一个Dog的实例,并调用它的方法
const dog = new Dog('旺财', 2)
dog.sayName() // My name is 旺财
dog.sayAge() // My age is 2
dog.colors.push('灰色')
console.log(dog.colors) // ['黑色', '白色', '棕色', '灰色']
const dog2 = new Dog('大黄', 3)
console.log(dog2.colors) // ['黑色', '白色', '棕色']
这样子类实例既可以继承父类的实例属性和方法,也可以继承父类原型中的属性和方法。
示例说明
示例一
下面我们来看一个更具体的例子,使用面向对象的方式实现一个圆形类和一个长方形类,来演示继承的使用。
// 定义一个Shape基类
class Shape {
constructor(color) {
this.color = color
}
draw() {
console.log(`画出一个${this.color}的形状。`)
}
}
// 定义一个Circle圆形类,继承Shape类
class Circle extends Shape {
constructor(color, r) {
super(color)
this.r = r
}
draw() {
console.log(`画出一个${this.color}的圆形,半径为 ${this.r}。`)
}
}
// 定义一个Rectangle长方形类,继承Shape类
class Rectangle extends Shape {
constructor(color, width, height) {
super(color)
this.width = width
this.height = height
}
draw() {
console.log(`画出一个${this.color}的长方形,宽度为 ${this.width},高度为 ${this.height}。`)
}
}
// 创建一个Circle圆形类和一个Rectangle长方形类的实例
const circle = new Circle('红色', 10)
circle.draw() // 画出一个红色的圆形,半径为 10。
const rectangle = new Rectangle('蓝色', 20, 30)
rectangle.draw() // 画出一个蓝色的长方形,宽度为 20,高度为 30。
示例二
继承也可以通过ES6的class和extends关键字来实现。
// 定义一个Animal类
class Animal {
constructor(name) {
this.name = name
}
// Animal类的实例方法
sayName() {
console.log(`My name is ${this.name}`)
}
}
// 定义一个Dog类,继承Animal类
class Dog extends Animal {
constructor(name, age) {
super(name)
this.age = age
}
// Dog类的实例方法
sayAge() {
console.log(`My age is ${this.age}`)
}
}
// 创建一个Dog的实例,并调用它的方法
const dog = new Dog('旺财', 2)
dog.sayName() // My name is 旺财
dog.sayAge() // My age is 2
总结
以上是JavaScript中几种常见的继承实现方式,不同的继承方式各有优缺点,在实际的开发中我们需要根据具体的情况选择不同的继承方式。同时在ES6中,类和继承的语法已经得到了进一步的简化,我们可以直接使用class和extends关键字来定义类和继承。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:浅谈JavaScript面向对象–继承 - Python技术站