持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第11天,点击查看活动详情
前言
继承是面向对象的特征之一,A 对象通过继承 B 对象,就能直接拥有 B 对象的所有属性和方法。这对于代码的复用是非常有用的。
大部分面向对象的编程语言,都是通过“类”(class)实现对象的继承。传统上,JavaScript 语言的继承不通过 class(ES6 引入了class 语法),而是通过“原型对象”(prototype)实现。那么在JS中常见的继承方式有几种呢?
原型链继承
- 核⼼:将⽗类实例作为⼦类原型;
- 优点:可以复用父类构造函数的⽅法;
- 缺点: 创建⼦类实例的时候,不能传⽗类的参数。 ⼦类实例共享了⽗类构造函数的引⽤属性,引⽤属性共享。 ⽆法实现多继承。
// 父类
function Parent (name){
this.name = name;
this.q = ['0'];
}
Parent.prototype.say = function (){
console.log('hi');
}
// 子类
function Child (){}
// 原型链继承
Child.prototype = new Parent();
// 子类实例
const xiaohong = new Child('小红');
const xiaoming = new Child('小名');
xiaohong.q.push('1');
console.log(xiaohong.name); //undefined
console.log(xiaoming.name); //undefined
//引⽤属性共享
console.log(xiaohong.q); //["0", "1"]
console.log(xiaoming.q); //["0", "1"]
console.log(xiaohong.q === xiaoming.q); //true
xiaohong.say(); //hi
xiaoming.say(); //hi
构造函数
- 核心:复制父类的实例给子类;
- 优点:实例之间独立, 引用属性不共享;
- 缺点:继承不了父类原型上的属性;
// 父类
function Parent (name){
this.name = name;
this.q = ['2'];
}
Parent.prototype.say = function (){
console.log('hi');
}
// 子类
function Child (name, num){
// 拷贝父类实例的属性和方法
Parent.call(this, name);
this.number = num;
}
// 子类实例
const xiaohong = new Child('小红', '01');
const xiaoming = new Child('小名', '02');
xiaoming.q.push('4');
console.log(xiaohong.name); //小红
console.log(xiaohong.number); // 01
console.log(xiaohong.q); // ["2"]
console.log(xiaoming.name); //小名
console.log(xiaoming.number); //02
console.log(xiaoming.q); //["2", "4"]
//继承不了⽗类原型上的属性
xiaohong.say(); // xiaohong.say is not a function
xiaoming.say(); // xiaoming.say is not a function
组合式继承
- 核⼼:通过调⽤⽗类构造函数,继承⽗类的属性并保留传参的优点;然后通过将⽗类实例作为 ⼦类原型,实现函数复⽤。
- 优点: 保留构造函数的优点:创建⼦类实例,可以向⽗类构造函数传参数。 保留原型链的优点:⽗类的⽅法定义在⽗类的原型对象上,可以实现⽅法复⽤。 不共享⽗类的引⽤属性。
- 缺点:调⽤了2次⽗类的构造⽅法,会存在⼀份多余的⽗类实例属性;
// 父类
function Parent (name){
this.name = name;
}
Parent.prototype.say = function (){
console.log('hi');
}
// 子类
function Child (name, num){
// 拷贝父类实例的属性和方法
Parent.call(this, name); //两次继承
this.number = num;
}
Child.prototype = new Parent(); //一次继承
// 子类实例
const xiaohong = new Child('小红', '01');
const xiaoming = new Child('小名', '02');
console.log(xiaohong.name); //小红
console.log(xiaohong.number); // 01
console.log(xiaoming.name); //小名
console.log(xiaoming.number); //02
xiaohong.say(); //hi
xiaoming.say(); //hi
console.log(xiaohong.say === xiaoming.say) ; //true
ES6 class
//父类
class People {
constructor(name){
this.name = name;
}
say(){
console.log('hi'+ this.name);
}
}
// 父类实例
const xiaohong = new People('小红');
console.log(xiaohong.name); //小红
xiaohong.say(); //hi小红
// 子类继承父类
class Student extends People {
constructor(name, number){
super(name);
this.number = number;
}
say(){
console.log(this.name + '学号:' + this.number);
}
}
// 子类实例
const xiaoming = new Student('小明', '001');
console.log(xiaoming.name); //小明
console.log(xiaoming.number); // 001
xiaoming.say(); //小明学号:001
本文到此结束
如果大家还有什么其他想法,欢迎在评论区交流!