JavaScript 的继承通过原型链和构造函数实现、可以采用多种方式如原型链继承、构造函数继承、组合继承、原型式继承、寄生式继承及寄生组合式继承。 其中,最常用的继承方式是组合继承,因为它结合了原型链和构造函数的优点,避免了各自的缺点。通过构造函数继承属性,通过原型链继承共享的方法和属性,这样即避免了原型链中子类型实例共享父类引用属性的问题,又实现了方法复用。
本文将通过各种常见的继承方式的详细实现来深入探讨JavaScript的继承机制。
一、原型链继承
原型链继承是最基本的JavaScript继承方法,它利用原型让一个引用类型继承另一个引用类型的属性和方法。
function Parent(){
this.parentProperty = true;
}
Parent.prototype.getParentProperty = function() {
return this.parentProperty;
};
function Child(){
this.childProperty = false;
}
// 继承Parent
Child.prototype = new Parent();
var instance = new Child();
alert(instance.getParentProperty()); // true
这种方法的主要缺点是包含引用类型值的原型属性会被所有实例共享,此外,创建子类型实例时,不能向超类型的构造函数中传递参数。
二、构造函数继承
构造函数继承使用父类的构造函数来增强子类实例,等同于复制父类的实例属性给子类,没有使用原型。
function Parent(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
function Child(name){
// 继承自Parent
Parent.call(this, name);
}
var instance1 = new Child("instance1");
var instance2 = new Child("instance2");
instance1.colors.push("black");
alert(instance1.colors); // "red,blue,green,black"
alert(instance2.colors); // "red,blue,green"
这种方法的优点是可以在子类型构造函数中向超类型构造函数传递参数。但这种方法的缺点是方法都在构造函数中定义,因此函数复用无从谈起。
三、组合继承
组合继承(伪经典继承)结合了原型链和构造函数的技术,从而发挥二者之长。用构造函数继承属性,用原型链的方式继承方法。
function Parent(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
Parent.prototype.sayName = function() {
return this.name;
};
function Child(name, age){
// 继承属性
Parent.call(this, name);
this.age = age;
}
// 继承方法
Child.prototype = new Parent();
Child.prototype.constructor = Child;
Child.prototype.sayAge = function() {
return this.age;
};
var instance = new Child("Nicholas", 29);
alert(instance.sayName()); // "Nicholas"
alert(instance.sayAge()); // 29
这是JavaScript中最常用的继承模式。不过,它也有自己的不足:使用Child的实例时,其原型上会存在两份相同的属性/方法。
四、原型式继承
原型式继承基于已有的对象创建新对象,同时还不必因此创建自定义类型。
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = Object.create(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
var yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
alert(person.friends); // "Shelby,Court,Van,Rob,Barbie"
Object.create()是ECMAScript 5中的一个新方法,直接指定原型。原型式继承的问题同原型链继承一样,包含引用类型值的属性始终都会共享相应的值,这点对于特定的应用场景是一种缺陷。
五、寄生式继承
寄生式继承是在原型式继承的基础上增强对象,返回构造函数。
function createAnother(original){
var clone = Object.create(original); // 通过调用函数创建一个新对象
clone.sayHi = function() { // 以某种方式来增强这个对象
alert("hi");
};
return clone; // 返回这个对象
}
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); // "hi"
这种方式同原型式继承几乎是一模一样的,只不过增强的是对象,而不是自定义类型的实例。
六、寄生组合式继承
寄生组合式继承是对组合继承的进一步优化,通过寄生方式来继承超类的原型,然后再将结果指定给子类的原型。
function inheritPrototype(subType, superType){
var prototype = Object.create(superType.prototype); // 创建对象
prototype.constructor = subType; // 增强对象
subType.prototype = prototype; // 指定对象
}
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
};
function SubType(name, age){
SuperType.call(this, name);
this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function(){
alert(this.age);
};
var instance = new SubType("Nicholas", 29);
instance.sayName(); // "Nicholas"
instance.sayAge(); // "29"
此方式核心代码段为 inheritPrototype
函数中,创建超类原型的一个副本,然后将其赋值给子类的原型,并且将构造器指向子类,目的是为了防止创建子类实例时超类的构造器被重写。寄生组合式继承被认为是引用类型继承的最理想方式。
相关问答FAQs:
如何使用JavaScript实现继承?
继承是面向对象编程中的一个重要概念,它允许一个对象(称为子类)继承另一个对象(称为父类)的属性和方法。在JavaScript中,有几种方式可以实现继承。
1. 原型链继承
原型链继承是JavaScript最基本的继承方式。它通过将子类的原型对象指向父类的实例来实现继承。这意味着子类将继承父类的属性和方法,包括原型链上的属性和方法。
2. 构造函数继承
构造函数继承是通过在子类的构造函数中调用父类的构造函数来实现继承。这样子类将获得父类的属性,并且每个实例都有自己的属性的副本。
3. 组合继承
组合继承是将原型链继承和构造函数继承结合起来使用的一种方式。它通过在子类的构造函数中调用父类的构造函数来继承属性,并且将其原型对象指向父类的实例来继承方法。
除了这些方式,还有其他的继承方式,如原型式继承、寄生式继承和寄生组合式继承。根据具体的需求和场景,选择适合的继承方式,可以让我们更好地利用JavaScript的面向对象特性。