- 例子: 比如说我们生活中的汽车就是一个类
- 轿车 和 货车 分别 继承汽车的属性 只不过 轿车 后面加的是 后备箱, 货车后面接的是大货箱, 使得轿车 和 货车 具备与父类不同的方法
- 定义: 继承可以是的子类别具有父类的各种方法和属性
第一种 原型链继承
- 原型链继承是比较常见的继承方式之一,其中涉及的构造函数,原型和实例三者之间的关系
- 每一个构造函数都有一个原型对象
- 原型对象有包含一个指向 构造函数的指针
- 而实例包含一个原型对象的指针
function Parent1 () {
this.name = 'parent1';
this.play = [1,2,3]
}
function Child1() {
this.type = 'child2'
}
Child1.prototype = new Parent1()
console.log(new Child1())
var s1 = new Child1()
var s2 = new Child1()
s1.play.push(4)
console.log(s1.play, s2.play)
原型继承会有一个问题
- 明明我只改变了 s1 为什么 s2 也发生了改变
- 因为两个实例使用的是一个原型对象,因此内存空间是共享的,当一个发生变化的时候,另一个也随之进行了变化
第二种 构造函数继承 (借助 call)
function Parent1() {
this.name = 'parent1'
}
Parent1.prototype.getName = function () {
return this.name
}
function Child1() {
Parent1.call(this);
this.type = 'child1'
}
let child = new Child1()
console.log(child)
console.log(child.getName())
- 子类拿到父类的属性值,避免了原型链继承的弊端,父类原型对象前,出现自己定义的方法,那么子类将无法继承这些方法
- 构造函数的优缺点
- 父类的引用属性不会被共享,解决了原型链继承的弊端,但随之而来的缺点也比较明显, 只能继承父类的实例属性和方法, 不能继承原型属性和方法
第三种 组合继承
function Parent3 () {
this.name = 'parent3';
this.play = [1,2,3]
}
Parent3.prototype.getName = function () {
return this.name
}
function Child3 () {
Parent3.call(this);
this.type = 'child3'
}
Child3.prototype = new Parent3()
Child3.prototype.constructor = Child3()
var s3 = new Child3()
var s4 = new Child3()
s3.play.push(4)
console.log(s3.play, s4.play)
console.log(s3.getName())
console.log(s4.getName())
- parent3 多构造了一次 会造成 内存上额外的开销
第四种 原型式继承
let parent4 = {
name: 'parent4',
friends: ['p1','p2','p3'],
getName: function () {
return this.name
}
}
let person4 = Object.create(parent4)
person4.name = 'tom'
parent4.friends.push('jerry')
let person5 = Object.create(parent4)
person5.friends.push('lucy')
console.log(person4.name)
console.log(person4.name === person4.getName())
console.log(person5.name)
console.log(person4.friends)
console.log(person5.friends)
- 不仅能继承属性 还能继承 getName 方法
- 但是 引用数组是共享的
- Object.create() 可以对一些对象实现浅拷贝的
- 那么这种继承方式的缺点也很明显
- 实例的引用,类型属性指向相同内存, 存在篡改的可能
第五种 寄生式继承
- 原型式继承可以获得一份目标对象的浅拷贝,然后利用这个浅拷贝的能力在进行增添一些方法
- 优点:
- 寄生式继承相比原型式继承,还是在父类基础上添加了更多的方法
let parent5 = {
name: 'parent5',
friends: ['p1','p2','p3'],
getName() {
return this.name
}
}
function clone (original) {
let clone = Object.create(original)
clone.getFriends = function () {
return this.friends
}
return clone
}
let person5 = clone(parent5)
console.log(person5.getName())
console.log(person5.getFriends())
第六种 寄生组合式继承
function clone(parent,child) {
// 这里用 Object.create 就可以减少组合式继承中多进行一次构造的过程
child.prototype = Object.create(parent.prototype)
child.prototype.constructor = child
}
function Parent6 () {
this.name = 'parent6'
this.play = [1,2,3]
}
Parent6.prototype.getName = function () {
return this.name
}
function Child6() {
Parent6.call(this)
this.friends = 'child5'
}
clone(Parent6,Child6)
Child6.prototype.getFriends = function () {
return this.friends
}
let person6 = new Child6()
console.log(person6)
console.log(person6.getName())
console.log(person6.getFriends())
第七种 可以通过 ES6 的 extends 关键是实现继承
class Person {
constructor(name) {
this.name = name
}
// 原型方法
// 既 Person.prototype.getName = function() {}
// 可以简写成 getName(){}
getName () {
console.log('Person',this.name)
}
}
class Gamer extends Person {
constructor(name,age) {
super(name);
this.age = age
}
}
const asuna = new Gamer ('Asuna', 20)
asuna.getName() // 成功访问到父类的方法
•
extends 底层也是使用的寄生组合式继承