定义父类
首先,需要定义一个父类,父类中包含一些属性和方法,这些属性和方法将被子类继承。
function Parent() {
this.parentProperty = 'This is a property of Parent';
}
Parent.prototype.parentMethod = function() {
console.log('This is a method of Parent');
};
定义子类并设置原型链
接下来,定义子类,并将子类的原型对象指向父类的一个实例,从而建立起原型链继承关系。
function Child() {
this.childProperty = 'This is a property of Child';
}
Child.prototype = new Parent();
// 修正子类原型的 constructor 属性,使其指向子类本身
Child.prototype.constructor = Child;
创建子类实例并使用继承的属性和方法
最后,创建子类的实例,并可以访问和调用从父类继承来的属性和方法,以及子类自身定义的属性和方法。
var child = new Child();
console.log(child.parentProperty);
console.log(child.childProperty);
child.parentMethod();
原理分析
- 在上述示例中,当创建子类
Child
的实例child
时,由于Child
的原型对象被设置为Parent
的一个实例,所以child
实例在查找属性和方法时,首先会在自身的属性中查找,如果找不到,则会沿着原型链向上查找,即查找Child.prototype
(也就是Parent
的实例)上的属性和方法,如果还找不到,则继续查找Parent.prototype
上的属性和方法,直到找到为止或者到达原型链的顶端(Object.prototype
)。 - 因此,
child
实例可以访问到父类Parent
中定义的parentProperty
属性和parentMethod
方法,同时也可以访问到子类Child
中定义的childProperty
属性。
注意事项
- 原型链上属性和方法的共享性:由于子类的原型对象是父类的一个实例,所以父类实例上的属性会被所有子类实例共享。如果父类实例的属性是一个对象或数组等引用类型,那么一个子类实例对该属性的修改可能会影响到其他子类实例。
function Parent() {
this.sharedArray = [1, 2, 3];
}
function Child() {
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
var child1 = new Child();
var child2 = new Child();
child1.sharedArray.push(4);
console.log(child2.sharedArray);
在上述示例中,child1
对 sharedArray
属性的修改会影响到 child2
,因为它们共享了同一个数组对象。
- 原型链的查找效率:随着原型链的增长,查找属性和方法的时间会增加。如果在多层继承的情况下,查找一个属性或方法可能需要遍历多个原型对象,这可能会影响性能。因此,在设计继承结构时,应尽量避免过长的原型链。
- 不能向父类构造函数传递参数:在原型链继承中,子类的原型对象是父类的一个实例,而这个实例是在子类定义时创建的,无法在创建子类实例时向父类构造函数传递参数。如果父类的初始化需要参数,那么这种继承方式可能不太适用,需要考虑使用其他继承方式,如构造函数继承或组合继承等。
使用原型链继承可以实现JavaScript中的继承关系,但需要注意其共享性、查找效率以及参数传递等问题,根据具体的应用场景合理地选择和使用继承方式,以满足代码的复用性和可维护性要求。