当需要实现JavaScript继承时,可以使用以下七种方式:
一、原型链继承
- 将父类的实例作为子类的原型
- 优点:父类的属性和方法能够被继承
- 缺点:
- 无法传递参数
- 所有子类实例共享父类引用类型属性,容易影响其他子类实例
示例代码:
// 父类
function Parent (name) {
this.name = name;
}
// 父类的方法
Parent.prototype.sayName = function() {
console.log('My name is ' + this.name);
};
// 子类
function Child() {}
// 子类的原型指向父类的实例,实现继承
Child.prototype = new Parent('Jack');
// 子类的实例
var child = new Child();
child.sayName(); // 输出"My name is Jack"
二、借用构造函数继承(经典继承)
- 在子类构造函数中调用父类构造函数,使用apply或call改变this的指向
- 优点:
- 可继承父类的属性和方法
- 父类的引用类型属性不会在子类间共享
- 缺点:子类不能访问到父类原型上的方法
示例代码:
// 父类
function Parent(name){
this.name = name;
this.colors = ['red', 'green', 'blue'];
}
// 子类,继承父类的colors属性
function Child(name, age){
Parent.call(this, name);
this.age = age;
}
// 子类实例
var child1 = new Child('A', 18);
var child2 = new Child('B', 20);
// 修改child1的引用类型属性,不影响child2
child1.colors.push('yellow');
console.log(child1.colors); // 输出 ["red", "green", "blue", "yellow"]
console.log(child2.colors); // 输出 ["red", "green", "blue"]
三、组合继承
- 结合原型链继承和构造函数继承的优点
- 父类构造函数继承属性,子类的原型继承方法
- 缺点:构造函数执行两次,父类指定的属性和方法会被子类实例化两次
示例代码:
// 父类
function Parent(name){
this.name = name;
this.colors = ['red', 'green', 'blue'];
}
// 父类的方法
Parent.prototype.sayName = function() {
console.log('My name is ' + this.name);
};
// 子类,借用构造函数继承父类的属性,使用原型链继承父类的方法
function Child(name, age){
Parent.call(this, name);
this.age = age;
}
Child.prototype = new Parent();
// 子类的方法
Child.prototype.sayAge = function() {
console.log('I am ' + this.age + ' years old.');
};
// 子类的实例
var child1 = new Child('A', 18);
var child2 = new Child('B', 20);
// 修改child1的引用类型属性,不影响child2
child1.colors.push("yellow");
console.log(child1.colors); // 输出 ["red", "green", "blue", "yellow"]
console.log(child2.colors); // 输出 ["red", "green", "blue"]
child1.sayName(); // 输出 "My name is A"
child1.sayAge(); // 输出 "I am 18 years old."
四、原型式继承
- 利用原型可以指向一个对象的特点,复制原对象,返回一个新的对象
- 缺点:引用类型会被子类共享
示例代码:
// 原对象
var person = {
name: "Tom",
colors: ["red", "blue", "green"]
};
// 继承方法
function object(o){
function F(){}
F.prototype = o;
return new F();
}
// 子类,通过复制原对象实现继承
var child1 = object(person);
var child2 = object(person);
// 修改child1的引用类型属性,影响child2
child1.colors.push("yellow");
console.log(child1.colors); // 输出 ["red", "blue", "green", "yellow"]
console.log(child2.colors); // 输出 ["red", "blue", "green", "yellow"]
五、寄生式继承
- 在原型式继承的基础上,增强对象,返回一个新的对象
- 缺点:引用类型会被子类共享
示例代码:
// 原对象
var person = {
name: "Tom",
colors: ["red", "blue", "green"]
};
// 继承方法
function object(o){
function F(){}
F.prototype = o;
return new F();
}
// 增强对象方法
function createAnother(o){
var clone = object(o);
clone.sayName = function(){
console.log("My name is " + this.name);
}
return clone;
}
// 子类,通过增强对象的方法实现继承
var child1 = createAnother(person);
var child2 = createAnother(person);
// 修改child1的引用类型属性,影响child2
child1.colors.push("yellow");
console.log(child1.colors); // 输出 ["red", "blue", "green", "yellow"]
console.log(child2.colors); // 输出 ["red", "blue", "green", "yellow"]
child1.sayName(); // 输出 "My name is Tom"
六、寄生组合式继承
- 在组合继承的基础上,将父类原型实例化的过程插入到创建子类的构造函数中
- 缺点:无
示例代码:
// 父类
function Parent(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
// 父类的方法
Parent.prototype.sayName = function(){
console.log("My name is " + this.name);
}
// 继承方法
function object(o){
function F(){}
F.prototype = o;
return new F();
}
function inheritPrototype(subType, superType){
var prototype = object(superType.prototype); // 创建父类原型的一个副本
prototype.constructor = subType; //这个副本的构造器指向子类,这样就可以继承父类原型中的方法了。
subType.prototype = prototype; // 修正子类的原型对象
}
// 子类,通过组合式继承实现继承
function Child(name, age){
Parent.call(this, name);
this.age = age;
}
inheritPrototype(Child, Parent);
// 子类的方法
Child.prototype.sayAge = function(){
console.log("I am " + this.age + " years old.");
}
// 子类的实例
var child1 = new Child("Tom", 18);
var child2 = new Child("Jerry", 20);
// 修改child1的引用类型属性,不影响child2
child1.colors.push("yellow");
console.log(child1.colors); // 输出 ["red", "blue", "green", "yellow"]
console.log(child2.colors); // 输出 ["red", "blue", "green"]
child1.sayName(); // 输出 "My name is Tom"
child1.sayAge(); // 输出 "I am 18 years old."
七、ES6 class 继承
- 使用关键字class和关键字extends实现继承
- 缺点:需先精通面向对象编程,但是它简化了构造函数继承和原型继承的写法,且具有清晰、美观的语法
示例代码:
// 父类
class Parent{
constructor(name){
this.name = name;
}
// 父类的方法
sayName(){
console.log("My name is " + this.name);
}
}
// 子类,通过ES6 class实现继承
class Child extends Parent{
constructor(name, age){
super(name);
this.age = age;
}
// 子类的方法
sayAge(){
console.log("I am " + this.age + " years old.");
}
}
// 子类的实例
var child1 = new Child("Tom", 18);
var child2 = new Child("Jerry", 20);
// 修改child1的引用类型属性,不影响child2
child1.colors.push("yellow");
console.log(child1.colors); // 输出 ["red", "blue", "green", "yellow"]
console.log(child2.colors); // 输出 ["red", "blue", "green"]
child1.sayName(); // 输出 "My name is Tom"
child1.sayAge(); // 输出 "I am 18 years old."
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:JavaScript实现继承的7种方式总结 - Python技术站