继承
●要知道什么是继承
●要知道继承的方式有哪些
●每种的继承方式是如何实现的
什么是继承
●继承关系出现在构造函数和构造函数之间
●当构造函数A 的实例使用了 构造函数B 的属性和方法
●我们就说 构造函数A 继承自 构造函数B
○管 构造函数A 叫做子类
○管 构造函数B 叫做父类
●总结 :继承就是获取存在对象已有属性和方法的一种方式
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.play = function () { console.log('玩游戏') }
// 将来我创建的 Person 的实例, 就会出现两个属性一个方法
const p = new Person('Jack', 18)
console.log(p)
function Student(classRoom) {
this.classRoom = classRoom
}
// 将来我创建的 Student 的实例
// 当我的 s 实例能使用 name 属性, age 属性 和 play 方法的时候
// 我们就说 Student 构造函数继承自 Person 构造函数
// Student 就是 子类
// Person 就是 父类
const s = new Student(2114)
console.log(s)
继承的方式有哪些
原型继承
●核心: 让子类的原型指向父类的实例
●优点:
○父类构造函数体内的属性和原型上的方法都可以实现继承
●缺点:
○继承下来的属性不在自己身上, 在自己的原型上
○一个构造函数的实例, 需要在两个地方传递参数
○所有子类的实例, name 和 age 一模一样
<script>
// 父类
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.play = function() {
console.log('玩游戏')
}
// 子类
function Student(classRoom) {
this.classRoom = classRoom
}
// 实现继承
const p = new Person('Jack', 18)
Student.prototype = p
// 此时 s 的 __proto__ 指向谁 ?
// 指向所属构造函数的 prototype
// 因为 Student.prototype 就是 就是 Person 的实例
// s 的 __proto__ 就是 Person 的实例
const s = new Student(2114)
console.log(s)
// 当你访问 s 的 classRoom 成员的时候
// 自己有直接使用
console.log(s.classRoom)
// 当你访问 s 的 name 成员的时候
// 自己没有, 去到自己的 __proto__ 上查找
// 因为自己的__proto__ 就是 Person 的实例
// 其实就是去到 Person 的实例上查找
console.log(s.name)
// 当你访问 s 的 play 成员的时候
// 自己没有, 去到自己的 __proto__ 上查找
// 也就是去到 Person 的实例上查找, 发现还是没有
// 就再去 __proto__ 上查找
// 自己的 __proto__ 的 __proto__
// Person 实例 的 __proto__
// Person 实例 的 __proto__ 就是 Person.prototype
s.play()
const s2 = new Student(2115)
console.log(s2)
</script>
借用构造函数继承
●核心: 把父类构造函数当做普通函数调用, 并且改变其 this 指向
●优点:
○子类的所有继承下来的属性都在自己身上
○子类的所有参数在一个地方传递
○子类的所有实例都可以给继承下来的属性赋不一样的值
●缺点:
○父类的原型上的方法没有继承下来
// 父类
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.play = function() {
console.log('玩游戏')
}
// 子类
function Student(classRoom, name, age) {
this.classRoom = classRoom
// 实现继承
Person.call(this, name, age)
}
const s = new Student(2114, 'Jack', 18)
console.log(s)
const s2 = new Student(2115, 'Rose', 20)
console.log(s2)
组合继承
●核心: 把原型继承和借用构造函数继承放在一起使用
●优点:
○都能继承下来
○属性在自己身上, 每一个子类实例继承的属性值都可以不一样
●缺点:
○子类的原型上多了一套属性
<script>
// 父类
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.play = function() {
console.log('玩游戏')
}
// 子类
function Student(classRoom, name, age) {
this.classRoom = classRoom
// 借用继承
// 目的: 把属性继承在自己身上
Person.call(this, name, age)
}
// 原型继承
// 目的: 继承父类原型上的方法
Student.prototype = new Person()
// 创建子类的实例
const s = new Student(2114, 'Rose', 20)
console.log(s)
</script>
ES6类继承
●类的继承是ES6中提出的一种继承方式
●这个继承有了语法的规定,必须要按照这样的方式来继承
●类的继承的实现: 两个步骤实现继承
○书写子类的时候, 加上 extends 关键字
■class 子类 extends 父类 {}
■目的: 继承父类原型上的方法
○在子类的 constructor 内书写 super()
■super(实参)
■目的: 继承父类的属性
●注意:
○必须要书写 super 和 extends
○在子类的 constructor 内 super 必须写在 this.xxx 的前面(最前面)
●父类可以是构造函数,但是子类不能的构造函数因为extends和super关键字就是给类设计的
<script>
// 父类
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
play() {
console.log('玩游戏')
}
}
// 父类
// function Person(name, age) {
// this.name = name
// this.age = age
// }
// Person.prototype.play = function () { console.log('玩游戏') }
// 子类
class Student extends Person {
constructor(classRoom, name, age) {
super(name, age)
this.classRoom = classRoom
}
study() {
console.log('学习')
}
}
const s = new Student(2114, 'Jack', 18)
console.log(s)
class Teacher extends Person {
constructor(gender, name, age) {
super(name, age)
this.gender = gender
}
}
const t = new Teacher('男', 'Jack', 20)
console.log(t)
</script>
拷贝继承
●利用 for in 循环遍历对象
●把所有的内容复制一份放在子类的原型上
// 书写 for in 循环的时候, 不光可以遍历到对象自己身上的属性, 也可以遍历到原型上的属性
// 父类
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.sayHi = function () { console.log('hello world') }
// 创建一个父类的实例
// const p = new Person('Jack', 18)
// console.log(p)
// for (let k in p) {
// console.log(k)
// }
// 子类
function Student(gender, ...arg) {
this.gender = gender
// 创建一个父类的实例
const p = new Person(...arg)
// 利用 for in 循环继承
for (let k in p) {
// 随着循环, k 分别是 name age 和 sayHi
Student.prototype[k] = p[k]
}
}
const s = new Student('男', 'Jack', 18)
console.log(s)
console.log(s.name)
const s2 = new Student('女', 'Rose', 20)
console.log(s2)
console.log(s2.name)
寄生式继承
/*
寄生式继承1
*/
// 父类
function Person(name) {
this.name = name
}
Person.prototype.sayHi = function () { console.log('hello world') }
// 子类
// 构造函数内不要写 return
function Student() {
// 直接在子类里面 return 一个父类的实例
const p = new Person('Jack')
return p
}
// 创建一个子类的实例
// 看似得到的是 Student 的实例, 但是其实得到的还是 Person 的实例
const s = new Student()
console.log(s)
/*
寄生式继承2 - 对寄生式继承1 的 改造
*/
// 父类
function Person(name) {
this.name = name
}
Person.prototype.sayHi = function () { console.log('hello world') }
// 子类
// 构造函数内不要写 return
function Student(gender) {
this.gender = gender
}
// 寄生一下 父类的原型
// Student的原型指向 Person的原型
Student.prototype = Person.prototype
// 创建一个子类的实例
// Student 自己没有原型使用了, 直接使用 Person 的原型
const s = new Student('男')
console.log(s)
// 寄生式组合继承
// 父类
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.sayHi = function () { console.log('hello world') }
// 实现继承
// 借助一个第三方构造函数
function Third() {}
// 第三方构造函数去继承 父类
// 利用寄生继承的方式来实现
// 第三方的 原型 指向 父类的原型
Third.prototype = Person.prototype
// 将来我使用第三方创建实例的时候
const t = new Third()
// 子类
function Student(...arg) {
// 利用 call 继承
Person.call(this, ...arg)
}
// 子类去想办法继承第三方的内容
// 利用原型继承去继承第三方内容
// 子类的原型指向第三方的实例
Student.prototype = new Third()
const s = new Student('Jack', 18)
console.log(s)
// 利用了一个自执行函数
// 自执行函数, 不需要名字的函数
;(function () {
var a = 100
console.log('你好 世界')
})()
// 子类
function Student(gender, ...arg) {
this.gender = gender
Person.call(this, ...arg)
}
// 把 第三方内容 放在自执行函数内
(function () {
function Third() {}
Third.prototype = Person.prototype
Student.prototype = new Third()
})()
const s = new Student('男', 'Jack', 18)
console.log(s)
冒充式继承
/*
冒充继承
+ 利用了一个浅拷贝 + 寄生继承
*/
// 父类
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.sayHi = function () { console.log('hello world') }
const p = new Person('Jack', 18)
// 寄生继承
function Student(gender) {
this.gender = gender
// 创建完毕实例以后, 拷贝一个父类的实例
Object.assign(this, p)
}
Student.prototype = Person.prototype
const s = new Student('男')
console.log(s)