JS Class继承
什么是继承
在面向对象编程中,继承是指一个对象(称作子类)通过复制另一个对象(称作父类)的属性和方法,从而拥有了父类的行为特征。继承允许子类重用父类的代码,并且可以根据需要添加或修改自己的属性和方法。
继承可以提高代码的复用性、可维护性和可扩展性。当一个类需要具有父类的部分或全部特性时,可以使用继承来实现。
原型链继承
在传统的 JavaScript 中,继承通常是通过原型链实现的。每个对象都有一个原型对象,可以作为该对象的父类或原型对象的子类的原型。通过原型链,子类可以继承父类的属性和方法。
示例代码
function Animal(name) {
this.name = name;
}
Animal.prototype.sayHi = function() {
console.log('Hi, I am ' + this.name);
}
function Cat(name) {
Animal.call(this, name);
}
Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;
Cat.prototype.meow = function() {
console.log('Meow');
}
var cat = new Cat('Tom');
cat.sayHi(); // Hi, I am Tom
cat.meow(); // Meow
代码说明
- 创建了一个
Animal
构造函数,接受一个name
参数,并将其赋值给实例的name
属性。Animal.prototype.sayHi
是一个方法,用来打印出实例的名字。 - 创建了一个
Cat
构造函数,继承了Animal
构造函数的属性和方法。使用Animal.call(this, name)
调用父类的构造函数,并将this
(即子类的实例)作为上下文,传入name
参数。 - 使用
Object.create(Animal.prototype)
将Cat.prototype
的原型对象设置为Animal.prototype
的实例,建立父子类原型对象之间的连接。然后通过Cat.prototype.constructor = Cat
解决constructor
属性指向问题,使得Cat.prototype
指向Cat
构造函数。 - 在
Cat.prototype
上添加一个meow
方法,定义猫独有的行为。 - 创建了一个名为
cat
的Cat
实例,并通过调用其继承自Animal
的sayHi
方法和猫自己的meow
方法分别输出字符串。
运行结果
Hi, I am Tom
Meow
代码分析
在原型链继承中,通过 Object.create(Animal.prototype)
将子类的原型对象指向父类的原型对象,从而实现继承。这样一来,子类的实例就能够访问到父类原型对象上的属性和方法,在子类的原型对象上添加的新方法也会被子类的实例继承。
原型链继承的优点是简单易懂,而且灵活性较高。但是也有一些缺点,其中之一是父类的构造函数会被多次调用。例如,在上面的示例中,我们调用 Animal.call(this, name)
既在 Cat.prototype
上调用了父类的构造函数又在 Cat
实例上调用了一次,导致了一次父类构造函数的重复执行。
构造函数继承
为了解决原型链继承中父类构造函数被多次调用的问题,可以使用构造函数继承。在构造函数继承中,通过在子类的构造函数中调用父类的构造函数来继承父类的属性。
示例代码
function Animal(name) {
this.name = name;
}
Animal.prototype.sayHi = function() {
console.log('Hi, I am ' + this.name);
}
function Cat(name) {
Animal.call(this, name);
}
var cat = new Cat('Tom');
cat.sayHi(); // Hi, I am Tom
代码说明
- 创建了一个
Animal
构造函数,接受一个name
参数,并将其赋值给实例的name
属性。Animal.prototype.sayHi
是一个方法,用来打印出实例的名字。 - 创建了一个
Cat
构造函数,调用Animal.call(this, name)
来继承父类的属性,并将this
(即子类的实例)作为上下文,传入name
参数。 - 创建了一个名为
cat
的Cat
实例,并通过调用其继承自Animal
的sayHi
方法输出字符串。
运行结果
Hi, I am Tom
代码分析
在构造函数继承中,通过在子类构造函数中调用父类的构造函数来继承父类的属性。这样做的好处是可以避免父类构造函数被多次调用的问题。
然而,构造函数继承也有缺点:子类无法继承父类原型对象上的方法。在上面的示例中,Cat
实例无法访问到 Animal.prototype
上的方法 sayHi
。
组合继承
为了克服原型链继承和构造函数继承的缺点,我们可以使用组合继承。组合继承是同时使用原型链继承和构造函数继承的一种模式。
示例代码
function Animal(name) {
this.name = name;
}
Animal.prototype.sayHi = function() {
console.log('Hi, I am ' + this.name);
}
function Cat(name) {
Animal.call(this, name);
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
var cat = new Cat('Tom');
cat.sayHi(); // Hi, I am Tom